看到這樣一段代碼
Promise.resolve().then(() => {
console.log(0);
return Promise.resolve(4) // p2
}).then((res) => {
console.log(res)
})
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() =>{
console.log(6);
})
result: 0123456
對此結(jié)果非常疑惑,百思不得其解,本來覺得結(jié)果應(yīng)該是0142356
最后經(jīng)過多方搜索,在知乎山看到這樣一個問題關(guān)于promise輸出順序的疑問?, 紫云飛大大的回答解決了我的疑惑,但是大大的解答太過抽象,我也是自己摸索好久才萬全明白,在此記錄一下。
問題的關(guān)鍵在于在promise內(nèi)部返回一個thenable對象會發(fā)生什么?
1、promise的then是一個microtask微任務(wù)
2、promise p return一個promise p2的時候,其實是將這個p2的then加入到了微任務(wù)隊列中,同時將p的resolve和reject傳給p2
3、p2中的then全部調(diào)用完成之后,會將p的resolve放入微任務(wù)隊列,resolve完成之后,這個時候p的狀態(tài)才會變化,繼續(xù)執(zhí)行p的then
所以以上代碼的執(zhí)行過程就是:
| 輸出 |
微任務(wù)隊列 |
|
[0, 1] |
| 0 |
[1, p2-then] |
| 1 |
[p2-then, 2] |
|
[2, resolve] |
| 2 |
[resolve, 3] |
|
[3, 4] |
| 3 |
[4, 5] |
| 4 |
[5, 6] |
| 5 |
[6] |
| 6 |
[] |
一下是幾個例子,加深一下理解(隊列中的元素對應(yīng)的是元素所在的then)
Example1
new Promise((resolve, reject) => {
console.log("外部promise");
resolve();
})
.then(() => {
console.log("外部第一個then");
return new Promise((resolve, reject) => {
console.log("內(nèi)部promise");
resolve();
})
.then(() => {
console.log("內(nèi)部第一個then");
})
.then(() => {
console.log("內(nèi)部第二個then");
});
})
.then(() => {
console.log("外部第二個then");
});
| 輸出 |
微任務(wù)隊列 |
| 外部promise |
[外部第一個then] |
| 外部第一個then |
[] |
| 內(nèi)部promise |
[內(nèi)部第一個then] |
| 內(nèi)部第一個then |
[內(nèi)部第二個then] |
| 內(nèi)部第二個then |
[resolve] |
|
[外部第二個then] |
| 外部第二個then |
[] |
Example2
new Promise((resolve, reject) => {
console.log("外部promise");
resolve();
})
.then(() => {
console.log("外部第一個then");
new Promise((resolve, reject) => {
console.log("內(nèi)部promise");
resolve();
})
.then(() => {
console.log("內(nèi)部第一個then");
})
.then(() => {
console.log("內(nèi)部第二個then");
});
})
.then(() => {
console.log("外部第二個then");
});
| 輸出 |
微任務(wù)隊列 |
| 外部promise |
[外部第一個then] |
| 外部第一個then |
[] |
| 內(nèi)部promise |
[內(nèi)部第一個then] |
|
[內(nèi)部第一個then, 外部第二個then] |
| 內(nèi)部第一個then |
[外部第二個then, 內(nèi)部第二個then] |
| 外部第二個then |
[內(nèi)部第二個then] |
| 內(nèi)部第二個then |
[] |
Example3
new Promise((resolve, reject) => {
console.log("外部promise");
resolve();
})
.then(() => {
console.log("外部第一個then");
let p = new Promise((resolve, reject) => {
console.log("內(nèi)部promise");
resolve();
})
p.then(() => {
console.log("內(nèi)部第一個then");
})
p.then(() => {
console.log("內(nèi)部第二個then");
});
})
.then(() => {
console.log("外部第二個then");
});
| 輸出 |
微任務(wù)隊列 |
| 外部promise |
[外部第一個then] |
| 外部第一個then |
[] |
| 內(nèi)部promise |
[內(nèi)部第一個then] |
|
[內(nèi)部第一個then,內(nèi)部第二個then] |
|
[內(nèi)部第一個then, 內(nèi)部第二個then, 外部第二個then] |
| 內(nèi)部第一個then |
[內(nèi)部第二個then, 外部第二個then] |
| 內(nèi)部第二個then |
[外部第二個then] |
| 外部第二個then |
[] |
Example4
let p = new Promise((resolve, reject) => {
console.log("外部promise");
resolve();
})
p.then(() => {
console.log("外部第一個then");
new Promise((resolve, reject) => {
console.log("內(nèi)部promise");
resolve();
})
.then(() => {
console.log("內(nèi)部第一個then");
})
.then(() => {
console.log("內(nèi)部第二個then");
});
})
p.then(() => {
console.log("外部第二個then");
});
| 輸出 |
微任務(wù)隊列 |
| 外部promise |
[外部第一個then, 外部第二個then] |
| 外部第一個then 內(nèi)部promise |
[外部第二個then, 內(nèi)部第一個then] |
| 外部第二個then |
[內(nèi)部第一個then, 內(nèi)部第二個then] |
| 內(nèi)部第一個then |
[內(nèi)部第二個then] |
| 內(nèi)部第二個then |
[] |
Example5
new Promise((resolve, reject) => { // p
console.log('外部promise');
resolve();
})
.then(() => {
console.log('外部第一個then');
new Promise((resolve, reject) => { // p2
console.log('內(nèi)部promise');
resolve();
})
.then(() => {
console.log('內(nèi)部第一個then');
return Promise.resolve(); // p3
})
.then(() => {
console.log('內(nèi)部第二個then');
})
})
.then(() => {
console.log('外部第二個then');
})
.then(() => {
console.log('外部第三個then');
})
| 輸出 |
微任務(wù)隊列 |
| 外部promise |
[外部第一個then] |
| 外部第一個then 內(nèi)部promise |
[內(nèi)部第一個then] |
|
[內(nèi)部第一個then, 外部第二個then] |
| 內(nèi)部第一個then |
[ 外部第二個then, p3-then] |
| 外部第二個then |
[p3-then, 外部第三個then] |
|
[外部第三個then, p2-resolve] |
| 外部第三個then |
[p2-resolve] |
|
[內(nèi)部第二個then] |
| 內(nèi)部第二個then |
[] |
Example6
new Promise((resolve, reject) => { // p
console.log("外部promise");
resolve();
})
.then(() => {
console.log("外部第一個then");
new Promise((resolve, reject) => { // p2
console.log("內(nèi)部promise");
resolve();
})
.then(() => {
console.log("內(nèi)部第一個then");
})
.then(() => {
console.log("內(nèi)部第二個then");
});
return new Promise((resolve, reject) => { // p3
console.log("內(nèi)部promise2");
resolve();
})
.then(() => {
console.log("內(nèi)部第一個then2");
})
.then(() => {
console.log("內(nèi)部第二個then2");
});
})
.then(() => {
console.log("外部第二個then");
});
| 輸出 |
微任務(wù)隊列 |
| 外部promise |
[外部第一個then] |
| 外部第一個then 內(nèi)部promise |
[內(nèi)部第一個then] |
| 內(nèi)部promise2 |
[內(nèi)部第一個then, 內(nèi)部第一個then2] |
| 內(nèi)部第一個then |
[內(nèi)部第一個then2, 內(nèi)部第二個then] |
| 內(nèi)部第一個then2 |
[內(nèi)部第二個then, 內(nèi)部第二個then2] |
| 內(nèi)部第二個then |
[內(nèi)部第二個then2] |
| 內(nèi)部第二個then2 |
[p-resolve] |
|
[外部第二個then] |
| 外部第二個then |
[] |
Example7
new Promise((resolve, reject) => { // p
console.log("外部promise");
resolve();
})
.then(() => {
console.log("外部第一個then");
new Promise((resolve, reject) => { // p2
console.log("內(nèi)部promise");
resolve();
})
.then(() => {
console.log("內(nèi)部第一個then");
})
.then(() => {
console.log("內(nèi)部第二個then");
});
return new Promise((resolve, reject) => { // p3
console.log("內(nèi)部promise2");
resolve();
})
.then(() => {
console.log("內(nèi)部第一個then2");
})
.then(() => {
console.log("內(nèi)部第二個then2");
});
})
.then(() => {
console.log("外部第二個then");
});
| 輸出 |
微任務(wù)隊列 |
| 外部promise |
[外部第一個then] |
| 外部第一個then 內(nèi)部promise |
[內(nèi)部第一個then] |
| 內(nèi)部promise2 |
[內(nèi)部第一個then, 內(nèi)部第一個then2] |
| 內(nèi)部第一個then |
[內(nèi)部第一個then2, 內(nèi)部第二個then] |
| 內(nèi)部第一個then2 |
[內(nèi)部第二個then, 內(nèi)部第二個then2] |
| 內(nèi)部第二個then |
[內(nèi)部第二個then2] |
| 內(nèi)部第二個then2 |
[p-resolve] |
|
[外部第二個then] |
| 外部第二個then |
[] |
2021年8月31日
最近手動寫了一遍Promise,可能有點晚。但是明白了為什么promise返回一個promise的時候會需要等一下才能顯示,因為Promise的resolvePromise(x, promise, resolve, reject)方法,如果x是一個promise,那么會調(diào)用一次then.call(x, y => {resolvePromise(y, promise, resolve, reject)}, reject), then是微服務(wù),所以需要再次進入微服務(wù)隊列排隊,所以會有這樣的現(xiàn)象