【问题标题】:To what extent are 'asynchronous functions' asynchronous in ECMAScript-2017?ECMAScript-2017 中的“异步函数”在多大程度上是异步的?
【发布时间】:2017-07-23 08:33:04
【问题描述】:

大约一个月前刚刚完成的 ECMAScript-2017 引入了 'asynchronous functions' 作为新功能。为了了解它们的“异步”程度,我在 Chrome 中进行了测试:

async function af1(){
   for (let i=0; i<300; i++)
      await (new Promise(
                (resolve,reject)=>{
                   for (let j=0; j<=4; j++) console.log(j);resolve();}))
            .then(()=>{for (let j=100; j<=400; j+=100) console.log(j);});;
}

async function af2(){
   for (let i=0; i<300; i++)
      await (new Promise(
                (resolve,reject)=>{
                   for (let j=5; j<=9; j++) console.log(j);resolve();}))
            .then(()=>{for (let j=500; j<=900; j+=100)  console.log(j);});
}
af1();
console.log(300);
af2();
console.log(400);
// 0 1 2 3 4 300 5 6 7 8 9 400 100 200 300 400 500 600 700 800 900
// 0 1 2 3 4 5 6 7 8 9 100 200 300 400 500 600 700 800 900
// 0 1 2 3 4 5 6 7 8 9 100 200 300 400 500 600 700 800 900
// 0 1 2 3 4 5 6 7 8 9 100 200 300 400 500 600 700 800 900
// 0 1 2 3 4 5 6 7 8 9 100 200 300 400 500 600 700 800 900
// 0 1 2 3 4 5 6 7 8 9 100 200 300 400 500 600 700 800 900
// ......

其实我期待一个更随机的序列。

现在,我可以有把握地说,代表 promise 或其中一个 then() 回调的每个代码块都是原子的,因为块内代码的执行不会被同一个程序?

【问题讨论】:

  • “异步”与“多线程”或“并行”没有任何关系。它始终是一个线程,并且所有同步代码块都以独占方式执行,直到它们完成。

标签: javascript asynchronous concurrency async-await


【解决方案1】:

编写异步代码并不意味着输出是随机的。当 Promise 立即解决(而不是依赖于一些不受控制的外部事件)时,事件的顺序实际上是可预测的:

在 Promise 构造函数回调函数中执行的所有内容都将在创建 Promise 时执行,即在继续执行 new Promise() 之后的代码之前同步运行。

然后执行其余的同步代码,直到调用堆栈为空。

然后,作为附加到当前任务的微任务的一部分,异步 then 回调将按照之前执行 then 方法的顺序执行。

await 关键字也会影响执行顺序:它们出现的async 函数将“停止”,让调用代码同步继续,就像async 函数执行了return (它返回一个承诺)。一旦提供给await 的承诺得到解决,async 函数将异步恢复其状态(在当前执行代码运行直到调用堆栈为空之后),并作为微任务的一部分继续。其时间与 then 回调的时间相同。

这解释了您的输出。不涉及随机性。

示例

比如说,在第一行输出的末尾,900,是什么表示执行回到af1()

在输出900的时候,队列中有两个微任务待处理:

1) af1 中的 then 回调已完成执行,返回值undefined(因为它没有return),这表示await 正在等待的承诺值。这等待异步处理,而当前代码仍在运行,直到调用堆栈为空。

2) af2 中的then 回调也已执行完毕:原理相同。

由于(1)在微任务队列中排在第一位,这是在输出 900 并且没有更多同步代码要执行(调用堆栈为空)之后发生的情况:

JavaScript引擎读取微任务队列,并拿微任务处理第一个await:函数状态恢复,包括其循环的状态,循环继续。这意味着 promise 构造函数回调是同步执行的,在输出的第二行中生成第一个值。 ...等等。

【讨论】:

  • 让我困惑的是“等待”。具体来说,它如何知道何时恢复执行?
  • @ChongLipPhang 当正在“等待”的承诺解决或拒绝时。
  • 我已经添加了一段关于此的内容。
  • 让我更清楚。比如说,在第一行输出 900 的末尾,是什么表示执行返回到 af1()?
  • 我添加了一段关于此的内容。顺序维护在一个微任务队列中(不是堆栈,它是 FIFO),它作为任务的一部分被消耗。
猜你喜欢
  • 1970-01-01
  • 2017-07-04
  • 2020-08-08
  • 2019-01-20
  • 1970-01-01
  • 2020-10-25
  • 2020-03-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多