[英]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
function 需要多少微任務才能解決。
看看這個(它是相同的代碼,但有 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
運算符消耗了一個。
我們還剩兩個。
To see that, in async2
, instead of creating a promise through the Promise
constructor, create a thenable (an object with a .then .then()
method, aka. promise-like object), that acts the same (well, a real promise is復雜得多,但為了這個例子,它是這樣工作的)。 我看起來像這樣:
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
語句之前返回 promise。 這是有道理的,但這也意味着,它們不能返回通過return
的相同promise object 。 他們也必須等待返回的 promise!
那么,讓我們看看我們的自定義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 , <--
表示日志;微任務用μ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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.