首页 > 解决方案 > 为什么这个异步函数没有异步运行?

问题描述

我正在学习 Javascript。我不习惯异步编程,所以在这里很难。这是我的代码:

var x = 0;

async function increment(x) {
  ++x;
  for (var i = 0; i < 999999999; i = i + 1);
  console.log('value of x is: ' + x);
};

increment(x);

console.log('Out of the function');

期望:“超出功能”会立即打印出来。'x 的值...' 需要一些时间。
现实:“x 的值...”在延迟后首先打印。然后打印'Out of the function'。

我的问题是,为什么不异步调用 increment() ?为什么它会阻止最后一个 console.log?

标签: javascriptnode.jsasynchronousasync-awaitsynchronization

解决方案


TL;DR:您必须await something在 async 函数中使其异步。

var x = 0;

async function increment(x) {
  await null;  // <-- ADD THIS LINE
  ++x;
  for (var i = 0; i < 10; i = i + 1);
  console.log('value of x is: ' + x);
};

increment(x);

console.log('Out of the function');

更长的版本:

首先让我们澄清一件事:JS 运行时是基于事件循环的单线程运行时,这意味着没有并发/并行代码执行(除非您使用Workeror child_process,这是另一个主题)。

现在到异步部分。async-await只是一个方便的语法糖,在后台使用and PromiseGenerator它反过来调用 platfrom 提供的事件循环调度 API。在 Node.js 中,APIprocess.nextTick在浏览器中是setImmediate.

通过这些 API 调度作业,缓存在微任务队列中,空闲时由事件循环点击并执行。因此,异步实际上只是调度。


有了这些知识,让我们回到你的案例。

关键字做了async function两件事:

  1. 它强制函数返回一个Promise
  2. 它允许您await在函数体内使用关键字

实际调度部分发生在您使用await关键字时。它的作用是将计划任务发送到微任务队列,这将始终是对调用的回调Promise.then()(在上面的示例中,我发送null,它将自动包装为Promise.resolve(null)),然后暂停当前函数的执行,并等到该承诺解决后继续执行其余的。

因此,如果您不使用await关键字涉及事件循环调度程序,则执行顺序将保持正常。从而解释你的情况。


如果您确实理解该async函数只是语法糖Promise,那么下一个经常出现的误解是new Promise(callback).

情况1:

new Promise((resolve) => {
  console.log('Bob comes first!')
  resolve(null)
})

console.log('Alice comes first!')

:谁先来?
:鲍勃先来。

案例二:

new Promise((resolve) => {
  resolve(null)
}).then(() => console.log('Bob comes first!'))


console.log('Alice comes first!')

:谁先来?
A : 这次爱丽丝先来。

案例 1 对应于您的代码。async将函数包装到 Promise 的回调中,但如果你不这样做.then(),回调会立即按正常顺序执行。

案例 2 对应于我的await null示例。它将函数体分成两部分,一个在里面new Promise(callback),其余的在里面promise.then(callback)。并且后者的执行顺序被推迟。


推荐阅读