都是本人理解,筆記大致概念,不詳細(xì)也并非完全正確,所以僅供參考。
同步、異步
JS 代碼的執(zhí)行,可以理解為任務(wù)的執(zhí)行,則其中有同步任務(wù)和異步任務(wù)。
同步任務(wù)是指在主線程上的任務(wù),只有前面一個(gè)執(zhí)行完畢,才會(huì)再執(zhí)行下一個(gè)。
同步任務(wù)好理解,異步任務(wù)的執(zhí)行主要是以下步驟:
- 主線程任務(wù)進(jìn)行,若發(fā)現(xiàn)異步任務(wù),將其移入任務(wù)隊(duì)列
- 主線程任務(wù)結(jié)束,開始執(zhí)行任務(wù)隊(duì)列的異步任務(wù)
- 任務(wù)隊(duì)列任務(wù)進(jìn)行,若發(fā)現(xiàn)異步任務(wù),將其移入任務(wù)隊(duì)列后
- 重復(fù)步驟3直至沒有任務(wù)隊(duì)列沒有任務(wù),結(jié)束。
Promise
都知道異步任務(wù)的 callback 的循環(huán)嵌套會(huì)讓人抓狂,所以就有了 Promise,Promise主要解決了JS異步任務(wù)代碼的可讀性問(wèn)題。
Promise 的三種狀態(tài)
- pending:初始狀態(tài),非成功或失敗
- fulfilled:操作成功完成
- rejected:操作失敗
Promise 的狀態(tài)的改變是不可逆的,所以它只會(huì)有:
- pending -> fulfilled
- pending -> rejected
同步、異步、Promise 的執(zhí)行順序
有個(gè)問(wèn)題,Promise 是不是異步操作?先看代碼:
setTimeout(() => {
console.log(1);
}, 0);
new Promise(resolve => {
console.log(2);
resolve();
console.log(3);
}).then(res => {
console.log(4);
});
console.log(5);
正確結(jié)果是:2、3、5、4、1 ,來(lái)一一解析。
先忽略1和5,如果說(shuō) Promise 是同步的,那么應(yīng)該是 2、4、3,但結(jié)果為什么是2、3、4?
猜想是 Promise 將 resolve(); 的方法加了某種延遲,這種延遲不加入任務(wù)隊(duì)列,而僅僅是等待 Promise 初始化函數(shù)結(jié)束而開始執(zhí)行,所以結(jié)果是 2、3、4。
實(shí)際上,這種操作是存在的,稱之為微任務(wù),微任務(wù)的執(zhí)行順序介于主線程和任務(wù)隊(duì)列之間。
PS:任務(wù)隊(duì)列的任務(wù)也稱之為宏任務(wù)
所以,在上述代碼中,1被插入任務(wù)隊(duì)列等待,而 Promise 初始函數(shù)先輸出 2,再因?yàn)?resolve(); 為微任務(wù)而先輸出3,然后因?yàn)槲⑷蝿?wù)的執(zhí)行順序低于主線程,所以輸出 5,最后微任務(wù)執(zhí)行完畢,執(zhí)行 then 輸出 4,最后才執(zhí)行到任務(wù)隊(duì)列,輸出 1,所以最后的結(jié)果是:2、3、5、4、1。
如果宏任務(wù)包含微任務(wù),那么先后順序是?
答案:執(zhí)行宏任務(wù) > 執(zhí)行包含的微任務(wù) > 執(zhí)行下一個(gè)宏任務(wù)。
new Promise(resolve1 => {
console.log(1);
setTimeout(() => {
resolve1();
}, 0);
setTimeout(() => {
console.log(2);
}, 0);
})
.then(() => {
return new Promise(resolve2 => {
resolve2();
console.log(3);
});
})
.then(() => {
console.log(4);
});
console.log(5);
答案?1、5、3、4、2
解析一下:主線程進(jìn)行,Promise 初始化函數(shù)里輸出 1,將宏任務(wù) setTimeout - resolve1(); 和setTimeout - 2 放入隊(duì)列,輸出 5,主線程結(jié)束,執(zhí)行宏任務(wù)setTimeout - resolve1(); ,執(zhí)行宏任務(wù)下的微任務(wù) resolve1() > then,新的 Promise 的執(zhí)行函數(shù)中,輸出 3,發(fā)現(xiàn)微任務(wù) resolve2(),執(zhí)行 then 輸出4,此宏任務(wù)結(jié)束,下一個(gè)宏任務(wù) setTimeout - 2 ,輸出 2,結(jié)束。
總結(jié)
Promise并非異步的,僅僅因?yàn)閞esolve和reject方法為微操作,所以會(huì)先執(zhí)行初始函數(shù)體,進(jìn)而再執(zhí)行then,所以會(huì)讓人誤以為是異步的。
ES5 寫 Promise
貼代碼總覺得不夠深層,寫一下自己的理解,參照 Promise 的三種狀態(tài),可以知道有這些屬性。
- 狀態(tài):記錄 Promise 的狀態(tài)
- 成功值:成功后的返回值
- 失敗值:失敗后的返回值
- 成功回調(diào)方法:成功后的執(zhí)行方法,即 then
- 失敗回調(diào)方法:失敗后的執(zhí)行方法,即 catch
- resolve方法:切換狀態(tài)至成功,并執(zhí)行成功回調(diào)方法
- reject方法:切換狀態(tài)至失敗,并執(zhí)行失敗回調(diào)方法
- then方法:傳入成功回調(diào)方法和失敗函數(shù)方法,將此存儲(chǔ)Promise中,切換狀態(tài)時(shí)進(jìn)行對(duì)應(yīng)調(diào)用。
過(guò)程
- 當(dāng) new 一個(gè) Promise 時(shí),將初始函數(shù)執(zhí)行,然后將 then 或 catch 的回調(diào)函數(shù)存儲(chǔ),當(dāng)切換狀態(tài)成功或切換狀態(tài)失敗函數(shù)時(shí),取出存儲(chǔ)的成功回調(diào)或失敗回調(diào)進(jìn)行執(zhí)行。
- 像 then 的鏈?zhǔn)秸{(diào)用,實(shí)質(zhì)是返回一個(gè)新的 Promise 即可。
- then 的支持傳入成功和失敗回調(diào),而 catch 實(shí)際上是執(zhí)行 then 且僅傳入失敗回調(diào)。