【问题标题】:Does all async function code will run i a different thread?所有异步函数代码都将运行不同的线程吗?
【发布时间】:2021-01-12 21:32:49
【问题描述】:

我写了以下代码:

async function imaAsyncFunction () {
    console.log('1234');
    let res = await setTimeout(()=>{console.log('10 sec');},10000);
    console.log(res);
    console.log('is After?!?');
}

imaAsyncFunction();
console.log('bla bla bla');

我有点惊讶 '1234' 根本没有打印出来。此外, console.log(res); 行是在 10 秒超时完成之前打印的,但我猜原因是 await 之后的代码没有返回 Promise 但是,为什么代码:console.log('bla bla bla'); 被打印在两行之前:

  console.log(res);
  console.log('is After?!?');

如果我们看到代码没有因为 setTimeout() 而推迟,那么为什么这两行代码在函数完成之前都没有运行,只有在 控制台之后才运行.log('bla bla bla'); 应该运行吗? 我在这里错过了什么?

【问题讨论】:

  • “是否所有的异步函数代码都会在不同的线程中运行?” 不,据我所知,JS 是单线程的
  • @JSONDerulo 希望我能支持你的用户名
  • setTimeout 不返回承诺。所以使用await setTimeout没有意义。
  • “我有点惊讶,'1234' 根本没有打印出来。” 是的。我已将您的代码复制到 Stack Snippet 中。如您所见,1234 已记录。

标签: javascript asynchronous async-await


【解决方案1】:

所有异步函数代码都会在不同的线程中运行吗?

没有。 async 函数、await 和 Promise 不会创建或使用不同的线程。

promise 本身不会使任何事情异步¹,它只是一种观察完成已已经异步的事情(例如您的计时器回调,它使用您环境的计时器子系统,以便稍后调用您的计时器回调)。

async 函数“只是”一种方式(一种非常非常有用的方式) 通过语法而不是通过使用 .then 或附加履行和拒绝处理程序来等待承诺解决.catch 方法。

console.log(res); 这行让我有点惊讶。在 10 秒的超时完成之前打印,但我猜原因是 await 之后的代码没有返回 Promise。

setTimeout 不会返回承诺,对。所以await 不会等待计时器回调发生。请参阅this question's answers,了解如何制作启用承诺的setTimeout 版本。

但是,为什么代码是:console.log('bla bla bla');在两行之前打印:

出于同样的原因:您的async 函数返回一个promise,而您调用async 函数的代码不会等待该promise 得到解决,然后再转到console.log('bla bla bla') 行。

这里的代码既使用了启用承诺的setTimeout 版本,又在执行console.log 之前等待async 函数的承诺解决:

function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function imaAsyncFunction () {
    let res = await delay(800); // 0.8s instead of 10s
    console.log("Timer expired");
    console.log(res); // undefined, since we haven't set a fulfillment value
    console.log("is after");
}

imaAsyncFunction()
.then(() => {
    console.log("bla bla bla");
})
.catch(error => {
    console.error(error);
});

如果您希望 delay 函数返回一个履行值,我们可以这样做:

function delay(ms, value) {
    return new Promise(resolve => setTimeout(() => {
        resolve(value);
    }, ms));
}

虽然在所有标准环境中(现在),setTimeout 接受一个值传递给它的回调,所以我们可以这样做:

function delay(ms, value) {
    return new Promise(resolve => setTimeout(resolve, ms, value));
}

function delay(ms, value) {
return new Promise(resolve => setTimeout(resolve, ms, value));
}
async function imaAsyncFunction () {
    let res = await delay(800, 42); // 0.8s instead of 10s
    console.log("Timer expired");
    console.log(res); // 42
    console.log("is after");
}

imaAsyncFunction()
.then(() => {
    console.log("bla bla bla");
})
.catch(error => {
    console.error(error);
});

在你说过的评论中:

我仍然不明白为什么这两行都没有在 `console.log('bla bla bla'); 之前运行?

这是您的原始代码:

async function imaAsyncFunction () {
    console.log('1234');
    let res = await setTimeout(()=>{console.log('10 sec');},10000);
    console.log(res);
    console.log('is After?!?');
}

imaAsyncFunction();
console.log('bla bla bla');

当您运行该代码时,会按顺序发生以下情况:

  1. 函数imaAsyncFunction 已创建。
  2. imaAsyncFunction() 的调用被执行:
    1. 函数的同步部分运行:
      • console.log('1234'); 被执行
      • setTimeout(()=>{console.log('10 sec');},10000) 被执行
      • setTimeout 返回undefined
    2. 已到达await(注意:为清楚起见,我将跳过下面的一些细节。)
      • 通常,当您执行await p 时,p 是一个承诺(或至少类似于承诺)。由于您已经在 undefined 上使用了它,因此 awaitundefined 包装在一个承诺中,就好像它调用了 Promise.resolve(undefined) 并将其用作 p 而不是 undefined
      • 函数中await 之后的所有代码都附加到p 作为p 满足时的处理程序(因为您没有使用try/catch - 这很好 -所以没有拒绝处理程序)。当您将履行或拒绝处理程序附加到承诺时,您会创建一个新承诺,该承诺会根据原始承诺发生的情况以及(如果相关)调用履行或拒绝处理程序时发生的情况来解决。让我们将这个新承诺称为p2
      • 通常,p 还不会被解决,但在这种情况下它是,所以履行处理程序(imaAsyncFunction 中的其余代码,在await 之后)计划运行一次当前同步工作完成。如果p 尚未解决,这将不会发生(稍后会在p 解决时发生。)
      • 由于函数中没有任何其他await 表达式,因此隐式创建的承诺imaAsyncFunction(我们称之为p3解析为p2。这意味着当p2 结算时,来自imaAsyncFunction 的promise 以相同的方式结算(它以与p2 相同的履行值履行,或以p2 的拒绝原因被拒绝)。
      • imaAsyncFunction 返回p3
  3. imaAsyncFunction的调用已经完成,所以下一条语句被执行:console.log('bla bla bla');
  4. 代码执行到了脚本的末尾,这意味着所有同步代码已经运行完毕。
  5. 运行上述步骤 2.2 中安排的履行处理程序代码:
    1. 它执行console.log(res)console.log('is After?!?')
    2. 它用undefined 实现p2(因为函数中没有return x)。
      • 这会安排p3 的履行,因为p3 已解析为p2。这样做会安排执行 p3 的履行处理程序,但它没有任何 - 在 imaAsyncFunction() 的返回值上没有使用 await.then.catch 等。李>

¹该声明有一个警告:当您将履行或拒绝处理程序附加到承诺时,对该处理程序的调用始终是异步的即使承诺已经解决。那是因为如果 promise 没有解决,它将是异步的,如果在某些情况下对处理程序的调用是同步的,但在其他情况下是异步的,那么它会很混乱,所以它总是异步完成的。但这实际上是关于 Promise 或 async 函数的唯一事情,它使任何事情异步,其余的只是观察已经异步的事情。

【讨论】:

  • 我无法理解:For the same reason. Your async function returns a promise, and your code calling your async function doesn't wait for that promise to be settled before moving on to the console.log('bla bla bla') line. 仍然无法理解为什么这两行都不会在 console.log('bla bla bla'); 之前运行
  • @Eitanos30 - 当您调用async 函数时,只有第一个awaitreturn(隐式或显式)之前的函数中的代码会同步运行。此时,async 函数返回一个承诺,其其余代码异步运行。承诺的确定取决于该代码中发生的情况。由于imaAsyncFunction(); 没有做任何事情来等待promise 被解决,所以运行下一行代码。
  • FWIW,我在我最近的书 JavaScript:新玩具 的第 8 章中详细介绍了 Promise。然后我继续第 9 章中的async/await。如果您有兴趣,请在我的个人资料中链接。
  • Crowder,如果我在等待之前添加第一行,那么 console.log('bla bla bla') 根本不会运行
  • @Eitanos30 - 如果您查看浏览器控制台,您会看到一个错误。您只能在 async 函数中使用 await A) 在模块中如果您的环境在模块中支持 top-level await。这就是为什么我在示例中没有使用await,而是使用.then.catch
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-08
  • 1970-01-01
  • 2020-12-31
  • 1970-01-01
  • 2014-03-20
  • 1970-01-01
相关资源
最近更新 更多