为什么这个异步函数会同步运行?

这是我的代码:

async function test() {
  console.log('TEST');
}

function go() {
  console.log('one');
  test();
  console.log('two');
}

go()

我已将该test函数标记为async,并且我没有awaitgo调用它的方法中使用,所以我希望输出是这样的:

one
two
TEST

但是输出是这样的:

one
TEST
two

在我的实际用例中,test是一个函数,它包装了一些我确实希望异步而不是按顺序发生的长处理逻辑。

为什么这里没有发生这种情况,我该如何解决?

回答

async函数体同步执行,直到第一条await语句。当其遇到出await表达它产生控制到调用函数和被挂起。await表达式后面的部分是异步完成的。

根据 MDN文档:

可以将 async 函数的主体视为由零个或多个 await 表达式拆分。顶级代码,直到并包括第一个 await 表达式(如果有),都是同步运行的。这样,没有 await 表达式的 async 函数将同步运行。但是,如果函数体内有 await 表达式,则 async 函数将始终异步完成。

每个 await 表达式之后的代码可以被认为存在于 .then 回调中。通过这种方式,通过函数的每个可重入步骤逐步构建一个承诺链。返回值形成链中的最后一个链接。

这可以通过以下没有 no 的示例观察到await,其中函数体是同步执行的:

async function testSync() {
  console.log("Runs synchronously");
}


console.log(1);

testSync();

console.log(2);

这是一个等待的Promise 在 之后使行为异步的示例await

async function testAsync() {
  console.log("Runs synchronously until here");
  await myPromise();
  console.log("Runs asynchronously here");
}

function myPromise() {
  return Promise.resolve();
}


console.log(1);

testAsync();

console.log(2);

这可以通过多种方式异步执行。您可以将函数包装在setTimeout调用中:

function test() {
  console.log("I want to execute asynchronously");
}

console.log(1);
setTimeout(test, 0);
console.log(2);

或者你可以通过承诺回调来做到这一点:

function test(){
 console.log("I want to run asynchronously");
}
console.log(1);
Promise.resolve().then(test).catch(e => console.error(e));
console.log(2);

同样可以使用queueMicrotask, docs here 完成:

function test(){
 console.log("I want to run asynchronously");
}
console.log(1);
queueMicrotask(test);
console.log(2);

setTimeout和使用 Promise 回调的方法之间的区别在于它们如何排队等待执行。有两个任务队列,一个是其中初始程序的执行,任务队列setTimeoutsetIntervalrequestAnimationframe等回调被排队,而另一个是microtask队列,其中承诺回调进行排队。

当你从排队的任务队列中的任务,有任务仍然留下,事件循环将检查microtask队列并执行所有从任务队列中占用的下一个任务之前。

这是一个演示这一点的片段:

function testTaskQueue(){
 console.log("Enqueued in the task queue");
}
function testMicroTaskQueue(){
 console.log("Enqueued in the microtask queue");
}

console.log(1);
setTimeout(testTaskQueue, 0);
Promise.resolve().then(testMicroTaskQueue);
queueMicrotask(testMicroTaskQueue);
console.log(2)

因此,根据这一点,您可以制作自己的异步包装器并将您的函数作为回调执行:

function makeAsync(callback, ...params) {
  //enqueued intask queue
  setTimeout(callback, 0, ...params);
  //Or using microtasks
  Promise.resolve().then(() => test(...params)).catch(console.error);
}

function test(...params) {
  console.log("I want to run asynchronoulsy", ...params);
}
console.log(1)
makeAsync(test, 3);
console.log(2);


以上是为什么这个异步函数会同步运行?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>