为什么 async 函数在 promise 稍后启动后完成执行?
Why does an async function finishes executing after a promise started later?
这是我不明白的事件循环。
代码如下:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 start');
return new Promise((resolve, reject) => {
resolve();
console.log('async2 promise');
})
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
}).then(function() {
console.log('promise3');
});
console.log('script end');
结果是:
script start
async1 start
async2 start
async2 promise
promise1
script end
promise2
promise3
async1 end
setTimeout
我不明白为什么在promise 2
和promise 3
之后打印async1 end
。
解释这个的事件循环中发生了什么?这些微任务在队列中的推送和弹出顺序是什么?
你很惊讶为什么 async1 end
出现在 promise2
和 promise3
之后,尽管它在它们之前被调用,并且微任务按照它们入队的顺序执行。
然而,这实际上归结为 async
函数需要执行多少微任务才能解决。
看看这个(它是相同的代码,但有 4 个原始承诺):
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 start');
return new Promise((resolve, reject) => {
resolve();
console.log('async2 promise');
})
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
}).then(function() {
console.log('promise3');
}).then(function() {
console.log('promise4');
});
console.log('script end');
/* Just to make the console fill the available space */
.as-console-wrapper {
max-height: 100% !important;
}
糟糕,async1 end
不再是末尾:它在 promise4
!
之前
那么这告诉我们什么? 这意味着 async1 end
在 3 个微任务之后被记录(不包括由 promiseN
引起的那些) .
这 3 个微任务需要什么? 让我们检查一下:
最后一个很明显:async1
中的 await
运算符消耗一个。
我们还有两个。
要看到这一点,在 async2
中,不是通过 Promise
构造函数创建承诺,而是创建一个 thenable(一个带有 .then()
方法,又名类似 promise 的对象),其行为相同(好吧,真正的 promise 要复杂得多,但为了这个例子,它以这种方式工作)。它看起来像这样:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 start');
console.log('async2 promise');
return {
then(resolve, reject){
queueMicrotask(() => {
resolve();
console.log('async2 resolve');
});
return Promise.resolve()
}
};
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
}).then(function() {
console.log('promise3');
}).then(function() {
console.log('promise4');
});
console.log('script end');
/* Just to make the console fill the available space */
.as-console-wrapper {
max-height: 100% !important;
}
但是,您可能会发现问题仍然存在。 promise2
仍然在 async2 resolve
之前调用。
async
函数 return 承诺 在 他们的 return
声明达成之前。这是有道理的,但这也意味着,他们不能 return 传递给 return
的 相同 承诺对象。他们也必须等待 returned 承诺,以便使他们的 returned 承诺反映其状态。
那么,让我们看看我们的自定义 then
函数何时被调用:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 start');
console.log('async2 promise');
return {
then(resolve, reject){
console.log('async2 then awaited')
queueMicrotask(() => {
resolve();
console.log('async2 resolve');
});
}
};
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
}).then(function() {
console.log('promise3');
}).then(function() {
console.log('promise4');
});
console.log('script end');
/* Just to make the console fill the available space */
.as-console-wrapper {
max-height: 100% !important;
}
啊哈,在新的微任务中!
我们找到了所有的漏洞,所以现在我们可以看到 async
东西是如何与 promise
交错执行的(这里 -->
意味着 enqueues, <--
表示 logs; microtasks 标记为 μt
):
MACROTASK #0
<-- script start
setTimeout enqueued, but it creates a MACROTASK, so it always comes at last --> MT#1
async1 called
<-- async1 start
async2 called
<-- async2 start
promise executor called synchronously
<-- async2 promise
resolved promise returned to async2
async2 execution halted --> μt#1
async1 execution halted at await
promise executor called synchronously
<-- promise1
promise1 resolved --> μt#2
`then` chain built
<-- script end
microtask #1
async2 continues, calls `then` of the returned promise
<-- async2 `then` awaited
promise's `then` enqueues microtask for calling callback of async2 --> μt#3
microtask #2
promise2 `then` called
<-- promise2
promise2 resolved --> μt#4
microtask #3
called queued callback of promise
<-- async2 resolve
async2 completes
promise returned by async2 resolves --> μt#5
microtask #4
promise3 `then` called
<-- promise3
promise3 resolved --> μt#6
microtask #5
async1 continues
<-- async1 end
async1 completes
microtask #6
promise4 `then` called
<-- promise4
promise4 resolved
MACROTASK #1
timer callback called
<-- setTimeout
这是我不明白的事件循环。
代码如下:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 start');
return new Promise((resolve, reject) => {
resolve();
console.log('async2 promise');
})
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
}).then(function() {
console.log('promise3');
});
console.log('script end');
结果是:
script start
async1 start
async2 start
async2 promise
promise1
script end
promise2
promise3
async1 end
setTimeout
我不明白为什么在promise 2
和promise 3
之后打印async1 end
。
解释这个的事件循环中发生了什么?这些微任务在队列中的推送和弹出顺序是什么?
你很惊讶为什么 async1 end
出现在 promise2
和 promise3
之后,尽管它在它们之前被调用,并且微任务按照它们入队的顺序执行。
然而,这实际上归结为 async
函数需要执行多少微任务才能解决。
看看这个(它是相同的代码,但有 4 个原始承诺):
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 start');
return new Promise((resolve, reject) => {
resolve();
console.log('async2 promise');
})
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
}).then(function() {
console.log('promise3');
}).then(function() {
console.log('promise4');
});
console.log('script end');
/* Just to make the console fill the available space */
.as-console-wrapper {
max-height: 100% !important;
}
糟糕,async1 end
不再是末尾:它在 promise4
!
那么这告诉我们什么? 这意味着 async1 end
在 3 个微任务之后被记录(不包括由 promiseN
引起的那些) .
这 3 个微任务需要什么? 让我们检查一下:
最后一个很明显:async1
中的 await
运算符消耗一个。
我们还有两个。
要看到这一点,在 async2
中,不是通过 Promise
构造函数创建承诺,而是创建一个 thenable(一个带有 .then()
方法,又名类似 promise 的对象),其行为相同(好吧,真正的 promise 要复杂得多,但为了这个例子,它以这种方式工作)。它看起来像这样:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 start');
console.log('async2 promise');
return {
then(resolve, reject){
queueMicrotask(() => {
resolve();
console.log('async2 resolve');
});
return Promise.resolve()
}
};
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
}).then(function() {
console.log('promise3');
}).then(function() {
console.log('promise4');
});
console.log('script end');
/* Just to make the console fill the available space */
.as-console-wrapper {
max-height: 100% !important;
}
但是,您可能会发现问题仍然存在。 promise2
仍然在 async2 resolve
之前调用。
async
函数 return 承诺 在 他们的 return
声明达成之前。这是有道理的,但这也意味着,他们不能 return 传递给 return
的 相同 承诺对象。他们也必须等待 returned 承诺,以便使他们的 returned 承诺反映其状态。
那么,让我们看看我们的自定义 then
函数何时被调用:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 start');
console.log('async2 promise');
return {
then(resolve, reject){
console.log('async2 then awaited')
queueMicrotask(() => {
resolve();
console.log('async2 resolve');
});
}
};
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
}).then(function() {
console.log('promise3');
}).then(function() {
console.log('promise4');
});
console.log('script end');
/* Just to make the console fill the available space */
.as-console-wrapper {
max-height: 100% !important;
}
啊哈,在新的微任务中!
我们找到了所有的漏洞,所以现在我们可以看到 async
东西是如何与 promise
交错执行的(这里 -->
意味着 enqueues, <--
表示 logs; microtasks 标记为 μt
):
MACROTASK #0
<-- script start
setTimeout enqueued, but it creates a MACROTASK, so it always comes at last --> MT#1
async1 called
<-- async1 start
async2 called
<-- async2 start
promise executor called synchronously
<-- async2 promise
resolved promise returned to async2
async2 execution halted --> μt#1
async1 execution halted at await
promise executor called synchronously
<-- promise1
promise1 resolved --> μt#2
`then` chain built
<-- script end
microtask #1
async2 continues, calls `then` of the returned promise
<-- async2 `then` awaited
promise's `then` enqueues microtask for calling callback of async2 --> μt#3
microtask #2
promise2 `then` called
<-- promise2
promise2 resolved --> μt#4
microtask #3
called queued callback of promise
<-- async2 resolve
async2 completes
promise returned by async2 resolves --> μt#5
microtask #4
promise3 `then` called
<-- promise3
promise3 resolved --> μt#6
microtask #5
async1 continues
<-- async1 end
async1 completes
microtask #6
promise4 `then` called
<-- promise4
promise4 resolved
MACROTASK #1
timer callback called
<-- setTimeout