在ECMAScript-2017中,“异步函数”在多大程度上是异步的?

时间:2017-07-23 08:33:05

标签: javascript asynchronous concurrency async-await

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()回调的每个代码块都是原子的,因为块内代码的执行不能被代码中的另一部分代码中断。同样的计划?

1 个答案:

答案 0 :(得分:4)

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

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

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

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

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

这解释了你的输出。没有随机性。

实施例

  

比如,在第一行输出结束时,900,是什么表示执行回到then

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

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

2)await中的then回调也已完成执行:原则相同。

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

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