javascript - 为什么这个异步函数没有异步运行?
问题描述
我正在学习 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?
解决方案
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 运行时是基于事件循环的单线程运行时,这意味着没有并发/并行代码执行(除非您使用Worker
or child_process
,这是另一个主题)。
现在到异步部分。async-await
只是一个方便的语法糖,在后台使用and Promise
,Generator
它反过来调用 platfrom 提供的事件循环调度 API。在 Node.js 中,APIprocess.nextTick
在浏览器中是setImmediate
.
通过这些 API 调度作业,缓存在微任务队列中,空闲时由事件循环点击并执行。因此,异步实际上只是调度。
有了这些知识,让我们回到你的案例。
关键字做了async function
两件事:
- 它强制函数返回一个
Promise
- 它允许您
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)
。并且后者的执行顺序被推迟。