看題!
new Promise((resolve, reject) => {
reject(1); // 確定promise狀態爲rejected
}).then((fullfilled) => {
console.log(fullfilled);
}, (rejected) => {
console.log(rejected, '2-1'); // 執行這裏,then方法必須返回一個新的Promise實例,這個沒有明式 return 值,便隱式觸發新promise的fullfilled狀態,進入下一個then的fullfilled回調
}).then((fullfilled) => {
console.log(fullfilled, '3-1'); // 執行這裏,因上一步resolve未傳值,所以fullfilled爲undefined,繼續隱式觸發fullfilled回調
}, (rejected) => {
console.log(rejected, '3-2');
}).then((fullfilled) => {
console.log(fullfilled, '4-1'); // 執行這裏
return Promise.reject(5); // 明式返回rejected的promise
}, (rejected) => {
console.log(rejected, '5-1');
}).catch((error) => {
console.log(error, '6-1'); // 執行這裏,被catch捕獲,error爲上一步reject傳入的值。因Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的別名,用於指定發生錯誤時的回調函數,所以catch同樣返回一個新的promise,若沒有顯示指定,便同上執行隱式操作
}).then(fullfilled => {
console.log(fullfilled, '7-1'); // 執行這裏
}, rejected => {
console.log(rejected, '7-2');
})
結果爲:
1 "2-1"
undefined "3-1"
undefined "4-1"
5 "6-1"
undefined "7-1"
總結:
Promise.prototype.then()
返回的是一個新的Promise實例(注意,不是原來那個Promise實例)。因此可以採用鏈式寫法,即then方法後面再調用另一個then方法。- then方法中若有顯式
return
一個新的 promise(包括fullfilled和rejected)則顯示執行。若無顯式指定,則隱式觸發新promise的pending->fullfilled
狀態變更,進入下一個then
的fullfilled
回調。 - 因
Promise.prototype.catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的別名,用於指定發生錯誤時的回調函數,所以catch同樣返回一個新的promise
,若沒有顯示指定,便執行隱式操作。
建議:
- 不要在then方法裏面定義 Reject 狀態的回調函數(即then的第二個參數),總是使用catch方法。
以上留有歧義者歡迎指出!
參考資料:
PromiseA+ 規範地址
中文翻譯PromiseA+ 規範
解讀Promise內部實現原理
這裏也結合promise提一下 event loop,JavaScript執行流程由函數調用棧與異步隊列(宏任務與微任務組成)。
注意這裏 macrotask microtask 分別表示異步任務的兩種分類。js運行過程中,JS 引擎會將所有任務按照類別分到兩個隊列中,首先在 macrotask 的隊列(也叫 task queue)中取出第一個任務,執行完畢後取出 microtask 隊列中的所有任務順序執行;之後再取 macrotask 任務,周而復始,直至兩個隊列的任務都取完。(任務的執行都是在函數調用棧中完成的)。
上一個例子:
console.log('start');
setImmediate(() => {
console.log(1);
new Promise((resolve, reject) => {
resolve(4);
}).then((data) => {
console.log(data);
})
});
setTimeout(() => {
console.log(2);
});
new Promise((resolve, reject) => {
resolve(3);
}).then((data) => {
console.log(data);
})
console.log('end');
結果:
start
end
3
1
4
2
其中chrome中,setImmediate優先於setTimeout執行