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



ECMAScript-2017 大约一个月前刚刚完成,引入了"异步函数"作为新功能。为了了解它们的"异步"程度,我在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
// ......

实际上,我期待一个更加随机的序列。

现在,我可以肯定地说,表示承诺或其 then(( 回调之一的每个代码块都是原子的,从某种意义上说,块内代码的执行不能被同一程序中代码的另一部分中断?

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

在 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 构造函数回调是同步执行的,在输出的第二行中生成第一个值。...等。

最新更新