为啥这个异步函数没有异步运行?
Posted
技术标签:
【中文标题】为啥这个异步函数没有异步运行?【英文标题】:Why is this async function not behaving asynchronously?为什么这个异步函数没有异步运行? 【发布时间】:2021-01-15 21:22:27 【问题描述】:我正在学习 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');
期望:'Out of the function' 会立即打印出来。 “x 的值...”需要一些时间。 现实:“x 的值...”在延迟后首先打印。然后打印'Out of the function'。
我的问题是,为什么不异步调用 increment()?为什么会阻塞最后一个console.log?
【问题讨论】:
大部分答案似乎都是关于解决方案的。但我更期待一些解释为什么我的期望不正确。我想每当我使用 async 时,该函数都不会阻止下一次同步执行——无论是否使用 await。我只是不想让增量函数阻止最后一个 console.log() 如果你把 async 放在没有 await 的地方,这个函数会表现得像一个普通的(同步)函数。因此,increment(x)
将阻止最后一个 console.log()。我在我的回答中添加了指向 MDN 的链接,可能有助于更好地理解。
@FarhanFuad 查看我的答案,尤其是 Bob-Alice 部分。
【参考方案1】:
TL;DR:您必须在 async 函数中 await something
使其异步。
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
或child_process
,这是另一回事主题)。
现在到异步部分。 async-await
只是一个方便的语法糖,它在底层使用了Promise
和Generator
,这反过来又调用了 platfrom 提供的事件循环调度 API。在 Node.js 中,API 是 process.nextTick
,而在浏览器中是 setImmediate
。
通过这些 API,作业被调度,缓存在 微任务队列 中,空闲时事件循环会点击并执行这些任务。因此,异步实际上只是调度。
有了这些知识,让我们回到你的案例。
async function
关键字做了两件事:
-
它强制函数返回一个
Promise
它允许你在函数体内使用await
关键字
当您使用await
关键字时,实际的调度部分就会发生。它的作用是将计划任务发送到微任务队列,这将始终是对Promise.then()
调用的回调(在上面的示例中,我发送null
,它将自动包装为Promise.resolve(null)
),然后暂停当前函数的执行,并等到该 promise 解决后再继续执行其余函数。
因此,如果您不使用 await
关键字涉及事件循环调度程序,则执行顺序将保持正常。从而解释你的情况。
如果你明白async
函数只是Promise
周围的语法糖,那么下一个常见的误解是new Promise(callback)
的执行顺序。
案例一:
new Promise((resolve) =>
console.log('Bob comes first!')
resolve(null)
)
console.log('Alice comes first!')
问:谁先来?答:鲍勃先来。
案例 2:
new Promise((resolve) =>
resolve(null)
).then(() => console.log('Bob comes first!'))
console.log('Alice comes first!')
问:谁先来?答:这次爱丽丝先来。
案例 1 对应于您的代码。 async
将函数包装到 Promise 的回调中,但如果您不这样做 .then()
,则该回调会立即按正常顺序执行。
案例 2 对应于我的 await null
示例。它将函数体分成两部分,一个在new Promise(callback)
内部,其余在promise.then(callback)
内部。并且后者的执行顺序是延迟的。
【讨论】:
【参考方案2】:async
函数没有await
,所以函数会同步运行。如果没有提供 await,javascript 不会抛出任何错误。
来自MDN
异步函数的主体可以被认为是被零分割 或更多等待表达式。***代码,直至并包括 第一个等待表达式(如果有的话),同步运行。在 这样,一个没有等待表达式的异步函数将运行 同步
将其转换为如下所示的内容,您将看到预期的行为:
var x = 0;
async function increment(x)
++x;
await setTimeout(function()
for (var i = 0; i < 999999999; i = i + 1);
);
console.log('value of x is: ' + x);
;
increment(x);
console.log('Out of the function');
【讨论】:
@adiga,这是传达概念的一个例子,我相信它可以达到目的。 @adiga,你的意思是如果我删除 await,它仍然会在“x 的值”之前记录“Out of the function”? 哦,别担心,伙计。我以为我在概念上错过了一些东西。 这是一个误导性的答案。您使用setTimeout
接入事件循环调度API,这确实会给您异步代码执行顺序,但这与async-await
机制无关。将两者放在一起非常令人困惑。
您可以await whateverAsLongAsItsAnExpression
使此示例正常运行。【参考方案3】:
因为我想 JavaScript 不是多线程的。
您可以尝试使用 JQuery 方法(也许)或了解 Web Workers 我发现谷歌搜索的第一个资源: https://medium.com/techtrument/multithreading-javascript-46156179cf9a
【讨论】:
【参考方案4】:你应该使用等待。来自官方 JS 文档:“关键字 await 使 JavaScript 等到该承诺完成并返回其结果。”
此外,如果它看起来有点奇怪,但您可以通过以下方式实现您的结果:
const x = 0;
async function f(x)
try
++x;
for (i = 0; i < 999; i = i + 1)
console.log('value of x is:', await i);
catch (e)
console.log('error', e);
;
f(x);
console.log('Out');
【讨论】:
Out
在 value of x is
之前被记录
是的,这基本上就是他的要求,即:“期望:'Out of the function' 会立即打印出来。'value of x...' 需要一些时间。”【参考方案5】:
async 关键字使函数返回一个promise 或'thenable'。你需要等待你的承诺增量。
建议了***等待,但在我们拥有它之前,您需要在另一个异步函数中调用或使用它。
https://github.com/tc39/proposal-top-level-await
var x = 0;
async function increment(x)
++x;
for (var i = 0; i < 999999999; i = i + 1);
console.log('value of x is: ' + x);
;
async function waitThenLog()
await increment(x);
console.log('Out of the function');
waitThenLog();
// or...
increment(x).then(() => console.log('Out of the function'));
【讨论】:
具有讽刺意味的是,为了达到预期的效果,您还可以在“异步函数增量”上省略异步,因为增量调用将被阻塞。【参考方案6】:这是因为异步函数并不意味着它会一直结束持续下去。
将异步 console.log 包装在 setTimeout 中,您将首先看到最后一个 console.log 打印。
var x = 0;
async function increment(x)
++x;
setTimeout(() =>
console.log('value of x is: ' + x);
, 1000);
;
increment(x);
console.log('Out of the function');
这是一个小提琴:https://jsfiddle.net/Ldjma3s8/
【讨论】:
以上是关于为啥这个异步函数没有异步运行?的主要内容,如果未能解决你的问题,请参考以下文章