大约一个月前刚刚定稿的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
// ......实际上我在期待一个更随机的序列。
现在,我是否可以有把握地说,每个代码块表示一个承诺或其当时()回调中的一个是原子的,也就是说,代码块中的代码的执行不会被同一程序中的代码的另一部分中断?
发布于 2017-07-23 08:50:50
编写异步代码并不意味着输出将是随机的。当承诺立即得到解决(而不是依赖于某个不受控制的外部事件)时,事件的顺序实际上是可预测的:
在允诺构造函数回调函数中执行的所有内容都将在创建承诺时执行,即在继续使用new Promise()后面的代码之前,它将同步运行。
然后执行其余的同步代码,直到调用堆栈为空。
然后,作为附加到当前任务的微任务的一部分,按照以前执行then方法的顺序执行异步then回调。
await关键字还将影响执行顺序:它们发生的async函数将“停止”,让调用代码同步地继续,就好像async函数已经执行了return (它返回一个承诺)。一旦解决了提供给await的承诺,async函数将异步恢复其状态(在当前执行的代码运行到调用堆栈为空),并继续作为微任务的一部分。这与then回调的时间相同。
这解释了你的输出。不涉及随机性。
示例
例如,在第一行输出的末尾,900,什么信号表明执行返回到
af1()?
在输出900时,队列中有两个挂起的微任务:
1) then回调在af1中已经完成执行,返回undefined的值(因为它没有return),这表示await正在等待的承诺值。这对于异步处理是挂起的,而当前代码在调用堆栈为空之前仍在运行。
2) then回调在af2中也完成了执行:相同的原则。
由于(1)是microtask队列中的第一个,这是在900输出之后发生的,并且没有更多的同步代码要执行(调用堆栈为空):
JavaScript引擎读取微任务队列并接受微任务来处理第一个await:函数状态被恢复,包括它的循环状态,并且循环继续。这意味着承诺构造函数回调将同步执行,从而产生输出第二行中的第一个值。...etc。
https://stackoverflow.com/questions/45263085
复制相似问题