摘要:異步請(qǐng)求線程在在連接后是通過(guò)瀏覽器新開一個(gè)線程請(qǐng)求將檢測(cè)到狀態(tài)變更時(shí),如果設(shè)置有回調(diào)函數(shù),異步線程就產(chǎn)生狀態(tài)變更事件,將這個(gè)回調(diào)再放入事件循環(huán)隊(duì)列中。
基礎(chǔ):
瀏覽器 -- 多進(jìn)程,每個(gè)tab頁(yè)獨(dú)立一個(gè)瀏覽器渲染進(jìn)程(瀏覽器內(nèi)核)
每個(gè)瀏覽器渲染進(jìn)程是多線程的,主要包括:
GUI渲染線程
JS引擎線程
也稱為JS內(nèi)核,負(fù)責(zé)處理Javascript腳本程序。(例如V8引擎)
JS引擎線程負(fù)責(zé)解析Javascript腳本,運(yùn)行代碼。
JS引擎一直等待著事件循環(huán)隊(duì)列中任務(wù)的到來(lái),然后加以處理,一個(gè)Tab頁(yè)(renderer進(jìn)程)中無(wú)論什么時(shí)候都只有一個(gè)JS線程在運(yùn)行JS程序
注意,GUI渲染線程與JS引擎線程是互斥的,所以如果JS執(zhí)行的時(shí)間過(guò)長(zhǎng),這樣就會(huì)造成頁(yè)面的渲染不連貫,導(dǎo)致頁(yè)面渲染加載阻塞。
事件觸發(fā)線程
歸屬于瀏覽器而不是JS引擎,用來(lái)控制事件循環(huán)(可以理解,JS引擎自己都忙不過(guò)來(lái),需要瀏覽器另開線程協(xié)助)
當(dāng)JS引擎執(zhí)行代碼塊如setTimeOut時(shí)(也可來(lái)自瀏覽器內(nèi)核的其他線程,如鼠標(biāo)點(diǎn)擊、AJAX異步請(qǐng)求等),會(huì)將對(duì)應(yīng)任務(wù)添加到事件線程中
當(dāng)對(duì)應(yīng)的事件符合觸發(fā)條件被觸發(fā)時(shí),該線程會(huì)把事件添加到事件循環(huán)隊(duì)列的隊(duì)尾,等待JS引擎的處理
注意,由于JS的單線程關(guān)系,所以這些待處理隊(duì)列中的事件都得排隊(duì)等待JS引擎處理(當(dāng)JS引擎空閑時(shí)才會(huì)去執(zhí)行)
定時(shí)器觸發(fā)線程
setInterval與setTimeout所在線程
瀏覽器定時(shí)計(jì)數(shù)器并不是由JavaScript引擎計(jì)數(shù)的,(因?yàn)镴avaScript引擎是單線程的, 如果處于阻塞線程狀態(tài)就會(huì)影響記計(jì)時(shí)的準(zhǔn)確)
因此通過(guò)多帶帶線程來(lái)計(jì)時(shí)并觸發(fā)定時(shí)(計(jì)時(shí)完畢后,添加到事件循環(huán)隊(duì)列中,等待JS引擎空閑后執(zhí)行)
注意,W3C在HTML標(biāo)準(zhǔn)中規(guī)定,規(guī)定要求setTimeout中低于4ms的時(shí)間間隔算為4ms。
異步HTTP請(qǐng)求線程
在XMLHttpRequest在連接后是通過(guò)瀏覽器新開一個(gè)線程請(qǐng)求
將檢測(cè)到狀態(tài)變更時(shí),如果設(shè)置有回調(diào)函數(shù),異步線程就產(chǎn)生狀態(tài)變更事件,將這個(gè)回調(diào)再放入事件循環(huán)隊(duì)列中。再由JavaScript引擎執(zhí)行。
正文:
異步 分塊程序、事件循環(huán)、并行程序中現(xiàn)在運(yùn)行的部分和將來(lái)運(yùn)行的部分之間的關(guān)系就是異步編程的核心
ajax請(qǐng)求的異步:發(fā)出請(qǐng)求時(shí),將來(lái)才能獲得請(qǐng)求返回結(jié)果
—— 應(yīng)該避免同步ajax請(qǐng)求,瀏覽器UI會(huì)被鎖定并阻塞所有用戶交互
異步基于事件循環(huán)機(jī)制 —— JavaScript引擎只是一個(gè)按需執(zhí)行代碼片段的環(huán)境,而這個(gè)需求是由其運(yùn)行環(huán)境決定的:當(dāng)遇到需要等待某些條件(網(wǎng)絡(luò)數(shù)據(jù)、定時(shí)器計(jì)時(shí)到期等),條件滿足后,運(yùn)行環(huán)境才會(huì)把回調(diào)函數(shù)插入到事件循環(huán)隊(duì)列中(參考基礎(chǔ)資料 事件觸發(fā)線程、定時(shí)器線程)
書中提到:ES6精確指定了事件循環(huán)的工作細(xì)節(jié),在技術(shù)上將其納入了javascript引擎的勢(shì)力范圍,不是只由宿主環(huán)境管理,怎么理解?? 和前文基礎(chǔ)部分對(duì)于瀏覽器渲染進(jìn)程包含的多個(gè)線程之間關(guān)系有一定出入么?
異步和并行意義完全不同??!
并行 —— 同步執(zhí)行 ,對(duì)于多線程編程,內(nèi)存的共享提升了復(fù)雜度
異步 —— 交替調(diào)度
Javascript是單線程執(zhí)行,從不跨線程共享數(shù)據(jù)
var a = 1,b=2 function foo(){ a = a + 1; b = b*2 } function bar(){ a = a * 2; b = b + 1; } // ajax(...)是某個(gè)庫(kù)中提供的函數(shù) ajax( "http://some.url.1", foo ); ajax( "http://some.url.2", bar );
由于js的單線程執(zhí)行特性,foo() bar()函數(shù)內(nèi)部的代碼具有原子性
雖然foo() bar()存在競(jìng)態(tài)導(dǎo)致 a,b的最終值并不確定
但這種不確定性是在函數(shù)執(zhí)行順序上的(兩個(gè)ajax返回的順序)
旨在說(shuō)明 “并發(fā)” 的幾種情況,在js中看似并發(fā)實(shí)際是由單線程事件循環(huán)機(jī)制實(shí)現(xiàn)的
均以兩個(gè)ajax請(qǐng)求的回調(diào)函數(shù)內(nèi)執(zhí)行不同代碼為例:
非交互: 兩個(gè)并發(fā)任務(wù)間彼此獨(dú)立不相關(guān),這時(shí)不確定性是完全可以接受的
交互: 并發(fā)任務(wù)間彼此通過(guò)作用域或者DOM間接交互,比如都向數(shù)組中插入一條數(shù)據(jù),這時(shí)數(shù)組中條目的順序就和并發(fā)任務(wù)執(zhí)行順序有關(guān)了,在需要保證正確交互順序的場(chǎng)景需要加入?yún)f(xié)調(diào)代碼
協(xié)作: 并發(fā)協(xié)作,將一個(gè)會(huì)長(zhǎng)期執(zhí)行的任務(wù)分割為多個(gè)步驟或多批任務(wù)執(zhí)行,以便占領(lǐng)事件循環(huán)隊(duì)列太久時(shí)間,如setTimeout(...0)進(jìn)行異步調(diào)度
任務(wù)隊(duì)列ES6引入,任務(wù)隊(duì)列(job queue) —— 掛在事件循環(huán)隊(duì)列的每個(gè)tick之后的一個(gè)隊(duì)列, 在事件循環(huán)的每個(gè)tick中,可能出現(xiàn)的異步動(dòng)作不會(huì)添加一個(gè)完整的新事件到事件循環(huán)隊(duì)列中,而是會(huì)在當(dāng)前tick的任務(wù)隊(duì)列末尾添加一個(gè)項(xiàng)目(一個(gè)任務(wù))
理論上說(shuō),任務(wù)隊(duì)列可能導(dǎo)致無(wú)限任務(wù)循環(huán)
回調(diào)函數(shù)是javascript異步的基本單元
這章主要討論回調(diào)這種自javascript誕生以來(lái)就存在的異步方式存在什么問(wèn)題(思維上的(大腦搞不定)、寫法上的(嵌套、硬編碼)、信任問(wèn)題(控制反轉(zhuǎn))等),以引出下一章對(duì)新的異步方式Promise的討論
給異步方法async傳入continuation,當(dāng)異步方法async結(jié)束時(shí)主動(dòng)調(diào)用continuation執(zhí)行
事實(shí)上async并不關(guān)心其結(jié)束對(duì)其他模塊有什么影響,但使用回調(diào)的方式,async在結(jié)束時(shí)必須主動(dòng)調(diào)用所有傳入的continuation
如果有數(shù)個(gè)異步任務(wù)需要鏈?zhǔn)綀?zhí)行,代碼寫法上很容易形成callback hell,但這不是重點(diǎn),即使把嵌套寫法解開寫成多個(gè)函數(shù)調(diào)用的方式,也同樣不易于閱讀;而且還有另一個(gè)問(wèn)題,當(dāng)鏈?zhǔn)交卣{(diào)中一個(gè)斷掉時(shí),如何處理錯(cuò)誤情況?需要硬編碼在各個(gè)函數(shù)中進(jìn)行處理,代碼復(fù)雜度大大增加
另外還有一個(gè)信任問(wèn)題,有些情況下異步任務(wù)是第三方提供,使用回調(diào)其實(shí)就是將代碼控制器交給了第三方處理——控制反轉(zhuǎn)
調(diào)用回調(diào)過(guò)早 調(diào)用回調(diào)過(guò)晚(或不被調(diào)用) 調(diào)用回調(diào)次數(shù)過(guò)少或過(guò)多 未能傳遞所需的環(huán)境和參數(shù) 吞掉可能出現(xiàn)的錯(cuò)誤和異常
針對(duì)回調(diào)的問(wèn)題可以用一些特定邏輯來(lái)解決:
比如回調(diào)傳入兩種:正確、錯(cuò)誤 或 error-first模式
針對(duì)回調(diào)過(guò)早問(wèn)題:強(qiáng)制回調(diào)封裝
但這些解決方案并不通用、且需要每次重復(fù)編寫、難以復(fù)用
幾個(gè)問(wèn)題為什么setTimeout(...)定時(shí)器可能精度不高?
setTimeout(...)只是保證了回調(diào)函數(shù)不會(huì)在指定時(shí)間間隔之前執(zhí)行,時(shí)間間隔之后插入到事件循環(huán)隊(duì)列中,但此時(shí)隊(duì)列中可能有多個(gè)項(xiàng)目
任務(wù)隊(duì)列和事件循環(huán)隊(duì)列是不同的概念,怎么區(qū)分?
個(gè)人理解:
事件循環(huán) -> 基于事件循環(huán)隊(duì)列,其維護(hù)者不是js引擎,當(dāng)js引擎執(zhí)行到需要等待某個(gè)條件完成的代碼時(shí)(ajax,setTimeout等),會(huì)交給當(dāng)前運(yùn)行環(huán)境執(zhí)行并提供一個(gè)回調(diào)函數(shù)(運(yùn)行環(huán)境可能使用其他線程),js引擎繼續(xù)執(zhí)行接下來(lái)的代碼;條件滿足后,運(yùn)行環(huán)境將回調(diào)函數(shù)插入到事件循環(huán)隊(duì)列的末尾,js引擎會(huì)從事件循環(huán)隊(duì)列中獲取代碼執(zhí)行
任務(wù)隊(duì)列 -> 首先明確任務(wù)隊(duì)列的位置:掛在事件循環(huán)隊(duì)列的每個(gè)tick之后的一個(gè)隊(duì)列,當(dāng)出現(xiàn)一個(gè)新的任務(wù)時(shí),總是掛到當(dāng)前事件循環(huán)tick結(jié)尾處!搞清楚以下代碼的執(zhí)行順序就能初步了解任務(wù)隊(duì)列與事件循環(huán)隊(duì)列的關(guān)系了:
console.log("A"); setTimeout( function(){ console.log("B"); },0) var p = new Promise((resolve, reject)=>{ console.log("C"); return Promise.resolve(console.log("D")); // 嵌套promise }) 輸出順序: A C D B
另外,任務(wù)隊(duì)列是由js引擎自己控制的 @TODO 了解具體實(shí)現(xiàn)方式
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/93687.html
摘要:這時(shí)候控制臺(tái)看到的是對(duì)象的快照,然而點(diǎn)開看詳情的話是這段代碼在運(yùn)行的時(shí)候,瀏覽器可能會(huì)認(rèn)為需要把控制臺(tái)延遲到后臺(tái),這種情況下,等到瀏覽器控制臺(tái)輸出對(duì)象內(nèi)容時(shí),可能已經(jīng)運(yùn)行,因此會(huì)在點(diǎn)開的時(shí)候顯示,這是的異步化造成的。 本書屬于基礎(chǔ)類書籍,會(huì)有比較多的基礎(chǔ)知識(shí),所以這里僅記錄平常不怎么容易注意到的知識(shí)點(diǎn),不會(huì)全記,供大家和自己翻閱; 上中下三本的讀書筆記: 《你不知道的JavaScri...
摘要:寫在前面這一章的順序?qū)τ谖唇佑|過(guò)使用過(guò)的童鞋而言略抽象了,前邊幾章主要為了說(shuō)明和之前的異步方式相比有什么優(yōu)勢(shì)和它能解決什么問(wèn)題,后邊才詳解的設(shè)計(jì)和各種場(chǎng)景下如何使用。建議先了解和簡(jiǎn)單使用過(guò)后再閱讀,效果更佳。 寫在前面:Promise這一章的順序?qū)τ谖唇佑|過(guò)使用過(guò)Promise的童鞋而言略抽象了,前邊幾章主要為了說(shuō)明Promise和之前的異步方式相比有什么優(yōu)勢(shì)和它能解決什么問(wèn)題,后邊才...
摘要:注此讀書筆記只記錄本人原先不太理解的內(nèi)容經(jīng)過(guò)閱讀你不知道的后的理解。作用域及閉包基礎(chǔ),代碼運(yùn)行的幕后工作者引擎及編譯器。 注:此讀書筆記只記錄本人原先不太理解的內(nèi)容經(jīng)過(guò)閱讀《你不知道的JS》后的理解。 作用域及閉包基礎(chǔ),JS代碼運(yùn)行的幕后工作者:引擎及編譯器。引擎負(fù)責(zé)JS程序的編譯及執(zhí)行,編譯器負(fù)責(zé)詞法分析和代碼生成。那么作用域就像一個(gè)容器,引擎及編譯器都從這里提取東西。 ...
摘要:閉包在循環(huán)中的應(yīng)用延遲函數(shù)的回調(diào)會(huì)在循環(huán)結(jié)束時(shí)才執(zhí)行事實(shí)上,當(dāng)定時(shí)器運(yùn)行時(shí)即使沒(méi)給迭代中執(zhí)行的是多有的回調(diào)函數(shù)依然是在循環(huán)結(jié)束后才會(huì)被執(zhí)行,因此會(huì)每次輸出一個(gè)出來(lái)。 閉包在循環(huán)中的應(yīng)用 延遲函數(shù)的回調(diào)會(huì)在循環(huán)結(jié)束時(shí)才執(zhí)行;事實(shí)上,當(dāng)定時(shí)器運(yùn)行時(shí)即使沒(méi)給迭代中執(zhí)行的是 setTime(..., 0),多有的回調(diào)函數(shù)依然是在循環(huán)結(jié)束后才會(huì)被執(zhí)行,因此會(huì)每次輸出一個(gè)6出來(lái)。 for(var...
摘要:有種內(nèi)置類型,分別是除對(duì)象之外,其他統(tǒng)稱為基本類型。另一個(gè)需要注意的是數(shù)組確切地說(shuō),數(shù)組也是的一個(gè)子類型我們可以通過(guò)下面的方法檢查變量是不是數(shù)組處理未聲明的變量時(shí),會(huì)返回這是因?yàn)橛幸粋€(gè)特殊的安全防范機(jī)制。 js有7種內(nèi)置類型,分別是undefined null boolean string number symbol object除對(duì)象之 Object 外,其他統(tǒng)稱為基本類型。符號(hào) ...
閱讀 2259·2023-04-26 01:50
閱讀 714·2021-09-22 15:20
閱讀 2595·2019-08-30 15:53
閱讀 1596·2019-08-30 12:49
閱讀 1714·2019-08-26 14:05
閱讀 2714·2019-08-26 11:42
閱讀 2309·2019-08-26 10:40
閱讀 2602·2019-08-26 10:38