Javascript 异步函数中的 Await 关键字未等待承诺按顺序完成

Await keyword in Javascript async function is not waiting for promises to complete in sequential order

我是 Javascript 的新手,正在尝试学习 promises 和 async/await 概念。我创建了三个承诺,如下所示。

const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("promise1");
        resolve("1");
    }, 1000)

});

const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("promise2");
        resolve("2");
    }, 5000)

});

const promise3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("promise3");
        resolve("3");
    }, 4000)

});

我创建了一个异步函数,它按顺序使用 await 进行多个承诺,如下所示。


async function example() {


    const result1 = await promise1;
    const result2 = await promise2;
    const result3 = await promise3;
    console.log(result1);
    console.log(result2);
    console.log(result3);
}

example();

浏览器控制台的输出是-

promise1
promise3
promise2
1
2
3

我不明白为什么在我的输出中 promise3 出现在 promise2 之前,因为在异步函数示例中的 await 语句序列中,promise2 出现在 promise3 之前?根据我的说法,输出应该如下所示 -

promise1
promise2
promise3
1
2
3

如有遗漏或错误,请指正。

const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("promise1");
        resolve("1");
    }, 1000)
});

当您构造一个承诺时,其中的代码立即运行。因此,一旦完成此行,计时器就会关闭并且 运行。您的代码预先创建了其中的 3 个承诺,这意味着它预先启动了所有 3 个计时器。

稍后当您 await 承诺时,这不会改变计时器正在做的事情。它只是让您的 async 函数知道它们何时完成。所以在后台,计时器将开始计时,将事情记录下来,并解决他们相应的承诺。即使没有等待承诺,这也可能发生。 1000ms 的超时将首先关闭,3 秒后 4000ms 的超时,然后 1 秒后 5000ms 的超时

如果您希望定时器仅在您到达异步函数的那一行时启动,那么您需要在异步函数的那一行执行 setTimeouts。例如:

async function example() {
  const result1 = await new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("promise1");
      resolve("1");
    }, 1000);
  });
  const result2 = await new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("promise2");
      resolve("2");
    }, 5000);
  });
  const result3 = await new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("promise3");
      resolve("3");
    }, 4000);
  });
  console.log(result1);
  console.log(result2);
  console.log(result3);
}

那是因为您在没有等待的情况下在异步函数之外创建 Promise。 Promise 内部的回调是 syncrounos。你运行 3个setTimeout的一个接一个。示例:

const x = new Promise(resolve => console.log(1))
console.log(2)

这会注销 1,2 - 与异步回调相反。在 setTimeout 中:

setTimeout(() => console.log(1));
console.log(2)

这会注销 2,1。

因此,如果您想要正确的行为,则必须在等待它们时创建 Promise,这是常见的做法:

function makePromise(time, number) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("promise", number);
      resolve(number);
    }, time);
  });
}


async function example() {
    const result1 = await makePromise(1000, 1);
    const result2 = await makePromise(5000, 2);
    const result3 = await makePromise(4000, 3);
    console.log(result1);
    console.log(result2);
    console.log(result3);
}

example();

简而言之,这是因为您首先创建了这三个承诺,并且在创建这三个承诺的过程中,您还启动了三个计时器。那三个定时器都启动了,都是运行并行的

然后,你await promise1。但是,该语句与计时器何时调用其回调完全无关。他们将完全靠自己来做。因此,计时器回调本身会根据每个计时器设置的预期时间创建 console.log() 输出(与您拥有的 await 完全无关)。

所以,之所以会这样,是因为您首先创建了所有三个承诺和计时器,然后才创建了 await。这里要理解的一件重要事情是“承诺不执行”。它们本身不是异步操作。一旦你执行 new Promise(),它就会调用 promise 执行器函数,你在那里的异步操作就会启动。从那时起,promise 所做的就是监视异步操作,然后在异步操作完成时通知观察者。

如果您在每个计时器启动时添加日志记录,您可以看到有关事物顺序的更多详细信息,这将显示所有三个计时器都已初始启动并且运行 并行调用它们的计时器回调,完全独立于代码后面的 await 语句:

const promise1 = new Promise((resolve, reject) => {
    console.log("starting timer 1");
    setTimeout(() => {
        console.log("promise1");
        resolve("1");
    }, 1000)

});

const promise2 = new Promise((resolve, reject) => {
    console.log("starting timer 2");
    setTimeout(() => {
        console.log("promise2");
        resolve("2");
    }, 5000)

});

const promise3 = new Promise((resolve, reject) => {
    console.log("starting timer 3");
    setTimeout(() => {
        console.log("promise3");
        resolve("3");
    }, 4000)

});

async function example() {
    const result1 = await promise1;
    const result2 = await promise2;
    const result3 = await promise3;
    console.log(result1);
    console.log(result2);
    console.log(result3);
}

example();

如果您更改了代码的结构,使您正在等待的函数实际创建并启动了计时器,那么您将不会启动第二个计时器,直到第一个计时器触发之后,您将拥有顺序计时器。

因此,如果您改为这样做,您的预期输出将会发生:

function runTimer1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("promise1");
            resolve("1");
        }, 1000)

    });
}

function runTimer2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("promise2");
            resolve("2");
        }, 5000)

    });
}

function runTimer3() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("promise3");
            resolve("3");
        }, 4000)

    });
}

async function example() {
    const result1 = await runTimer1();
    const result2 = await runTimer2();
    const result3 = await runTimer3();
    console.log(result1);
    console.log(result2);
    console.log(result3);
}

example();