繁体   English   中英

为什么这个异步函数没有异步运行?

[英]Why is this async function not behaving asynchronously?

我正在学习 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:您必须在 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 运行时是基于事件循环的单线程运行时,这意味着没有并发/并行代码执行(除非您使用Workerchild_process ,这是另一个主题)。

现在到异步部分。 async-await只是一个方便的语法糖,它在底层使用PromiseGenerator ,后者又调用 platfrom 提供的事件循环调度 API。 在 Node.js 中,API 是process.nextTick ,而在浏览器中它是setImmediate

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


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

async function关键字做了两件事:

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

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

因此,如果您不使用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)中。 并且后者的执行顺序被推迟。

async函数没有await ,因此函数将同步运行。 如果没有提供 await,javascript 不会抛出任何错误。

来自MDN

可以认为异步函数的主体被零个或多个等待表达式分割。 顶级代码,直到并包括第一个等待表达式(如果有),都是同步运行的。 这样,没有 await 表达式的 async 函数将同步运行

将其转换为如下所示的内容,您将看到预期的行为:

 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');

因为我想 JavaScript 不是多线程的。

您可以尝试使用 JQuery 方法(也许)或了解 Web Workers 我在谷歌上找到的第一个资源: https ://medium.com/techtrument/multithreading-javascript-46156179cf9a

那是因为异步函数并不意味着它总是最后结束

将异步 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/

你应该使用等待。 来自官方 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');

async 关键字使函数返回一个承诺或“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'));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM