使用并深入Javascript的異步機(jī)制
也沒個(gè)分級(jí)標(biāo)題,煩啊,誰去鼓搗你那字體大小啊

js運(yùn)行時(shí):上下文、事件循環(huán)
js在設(shè)計(jì)之初被設(shè)計(jì)為單線程語言,而現(xiàn)代瀏覽器的js環(huán)境支持多線程應(yīng)用,其中使用了一些方法突破了傳統(tǒng)單線程語言的限制。
js在執(zhí)行時(shí)運(yùn)行在執(zhí)行上下文當(dāng)中,代碼主體存在于全局上下文,每個(gè)函數(shù)存在于自己的本地上下文,eval()
函數(shù)會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文。上下文本質(zhì)上就是作用域,每個(gè)代碼段開始執(zhí)行時(shí)會(huì)創(chuàng)建新的上下文,代碼退出時(shí)銷毀該上下文。多個(gè)上下文之間的關(guān)系類似x86的調(diào)用棧。
js運(yùn)行時(shí)維護(hù)了一組代理以執(zhí)行js代碼,每個(gè)代理由以下幾部分構(gòu)成:
執(zhí)行上下文的集合、執(zhí)行上下文棧
主線程、可能創(chuàng)建用于執(zhí)行worker的額外線程集合
一個(gè)任務(wù)隊(duì)列、一個(gè)微任務(wù)隊(duì)列
除了主線程外,代理的其他組成部分對(duì)該代理來說都是唯一的。
每個(gè)代理由事件循環(huán)所驅(qū)動(dòng),事件循環(huán)負(fù)責(zé)收集事件、排列任務(wù),然后執(zhí)行等待中的js宏任務(wù),再執(zhí)行微任務(wù),最后執(zhí)行一些渲染繪制操作,然后進(jìn)入下一個(gè)循環(huán)。web應(yīng)用和瀏覽器的GUI運(yùn)行在主線程當(dāng)中,共享事件循環(huán)。事件循環(huán)分為三類:
Window事件循環(huán):驅(qū)動(dòng)所有同源窗口。
Worker事件循環(huán):驅(qū)動(dòng)worker的事件循環(huán),包括web worker/shared worker/service worker,Worker被放在一或多個(gè)代理中,獨(dú)立于代碼主體,被瀏覽器以一或多個(gè)事件循環(huán)處理。
Worklet事件循環(huán):驅(qū)動(dòng)worklet的代理。

任務(wù)(宏任務(wù))與微任務(wù)
當(dāng)執(zhí)行來自任務(wù)隊(duì)列中的任務(wù)時(shí),在每一次新的事件循環(huán)開始迭代的時(shí)候運(yùn)行時(shí)都會(huì)執(zhí)行隊(duì)列中的每個(gè)任務(wù)。在每次迭代開始之后加入到隊(duì)列中的任務(wù)需要在下一次迭代開始之后才會(huì)被執(zhí)行.
每次當(dāng)一個(gè)任務(wù)退出且執(zhí)行上下文為空的時(shí)候,微任務(wù)隊(duì)列中的每一個(gè)微任務(wù)會(huì)依次被執(zhí)行。不同的是它會(huì)等到微任務(wù)隊(duì)列為空才會(huì)停止執(zhí)行——即使中途有微任務(wù)加入。換句話說,微任務(wù)可以添加新的微任務(wù)到隊(duì)列中,并在下一個(gè)任務(wù)開始執(zhí)行之前且當(dāng)前事件循環(huán)結(jié)束之前執(zhí)行完所有的微任務(wù)。
順序總結(jié)
如果新任務(wù)是宏任務(wù),添加到新的宏任務(wù)隊(duì)列中
如果新任務(wù)是微任務(wù),添加到微任務(wù)隊(duì)列中
執(zhí)行當(dāng)前宏任務(wù)隊(duì)列,當(dāng)需要添加一個(gè)新任務(wù)時(shí):
當(dāng)前宏任務(wù)隊(duì)列執(zhí)行結(jié)束
執(zhí)行微任務(wù)隊(duì)列,新的微任務(wù)會(huì)被添加到當(dāng)前微任務(wù)隊(duì)列中繼續(xù)執(zhí)行
當(dāng)前微任務(wù)隊(duì)列執(zhí)行結(jié)束,切換到新的宏任務(wù)隊(duì)列重復(fù)上述步驟
宏任務(wù)

微任務(wù)

二者對(duì)比


Promise
Promise具有三種狀態(tài),pending、fulfilled、rejected:
pending:Promise的初始狀態(tài),既沒有被兌現(xiàn)也沒有被拒絕,表示請(qǐng)求還在進(jìn)行中
fulfilled:已兌現(xiàn),表示請(qǐng)求成功完成,調(diào)用
then()
處理函數(shù),Promise會(huì)向其中傳入包含服務(wù)器響應(yīng)的Response對(duì)象rejected:已拒絕,表示請(qǐng)求失敗,調(diào)用
catch()
處理函數(shù)fulfilled和rejected有時(shí)使用settled狀態(tài)來概括表示(與pending對(duì)應(yīng))。
Promise.all()
方法接收一個(gè)Promise array/map/set(下用數(shù)組代指),返回一個(gè)單一的Promise,用于實(shí)現(xiàn)多個(gè)異步函數(shù)的調(diào)用,當(dāng)且僅當(dāng)數(shù)組中所有Promise都被兌現(xiàn)時(shí),才會(huì)調(diào)用then()
并傳入包括了全部響應(yīng)的數(shù)組,其順序與傳入all()
的Promise順序相同;數(shù)組中任何一個(gè)Promise被拒絕時(shí)都會(huì)調(diào)用catch()
,并傳入該P(yáng)romise拋出的錯(cuò)誤。
Promise.any()
方法與Promise.all()
方法相對(duì)應(yīng),當(dāng)且僅當(dāng)數(shù)組中所有Promise都被拒絕時(shí),才會(huì)調(diào)用catch()
;當(dāng)任何一個(gè)Promise被兌現(xiàn)時(shí)都會(huì)調(diào)用then()
async/await
在函數(shù)開頭添加async可以使其成為一個(gè)異步函數(shù);在異步函數(shù)中,可以在調(diào)用返回Promise的函數(shù)前使用await,此時(shí)代碼會(huì)等待直到Promise完成,由此實(shí)現(xiàn)異步函數(shù)的同步阻塞。async函數(shù)的返回值總是一個(gè)Promise對(duì)象
async/await本質(zhì)是Promise的封裝,await后面的代碼相當(dāng)于Promise.then的回調(diào)
await fetch()
會(huì)直接返回response對(duì)象,使用await時(shí)使用try-catch方式捕獲異常

Promise()構(gòu)造器
Promise()
構(gòu)造器使用單個(gè)函數(shù)作為參數(shù)。該函數(shù)稱作executer。當(dāng)創(chuàng)建新的promise時(shí)需要實(shí)現(xiàn)這個(gè)執(zhí)行器。executer接收兩個(gè)函數(shù)作為參數(shù),兩個(gè)參數(shù)通常被稱作resolve
和reject
。
在executer中,手動(dòng)進(jìn)行異步函數(shù)的調(diào)用以及resolve和reject的調(diào)用,如果executer出現(xiàn)異常則自動(dòng)調(diào)用reject。