你不知道的JS讀書(shū)筆記(中)——異步

基礎(chǔ):
瀏覽器 -- 多進(jìn)程惨好,每個(gè)tab頁(yè)獨(dú)立一個(gè)瀏覽器渲染進(jìn)程(瀏覽器內(nèi)核)

每個(gè)瀏覽器渲染進(jìn)程是多線程的品擎,主要包括:
GUI渲染線程

JS引擎線程

  • 也稱(chēng)為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),需要瀏覽器另開(kā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ò)單獨(dú)線程來(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ò)瀏覽器新開(kāi)一個(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ì)被鎖定并阻塞所有用戶(hù)交互

異步基于事件循環(huán)機(jī)制 —— JavaScript引擎只是一個(gè)按執(zhí)行代碼片段的環(huán)境环肘,而這個(gè)需求是由其運(yùn)行環(huán)境決定的:當(dāng)遇到需要等待某些條件(網(wǎng)絡(luò)數(shù)據(jù)欲虚、定時(shí)器計(jì)時(shí)到期等),條件滿(mǎn)足后悔雹,運(yùn)行環(huán)境才會(huì)把回調(diào)函數(shù)插入到事件循環(huán)隊(duì)列中(參考基礎(chǔ)資料 事件觸發(fā)線程复哆、定時(shí)器線程)

書(shū)中提到:ES6精確指定了事件循環(huán)的工作細(xì)節(jié)欣喧,在技術(shù)上將其納入了javascript引擎的勢(shì)力范圍,不是只由宿主環(huán)境管理梯找,怎么理解唆阿?? 和前文基礎(chǔ)部分對(duì)于瀏覽器渲染進(jìn)程包含的多個(gè)線程之間關(guān)系有一定出入么锈锤?

異步并行意義完全不同Q北睢!
并行 —— 同步執(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返回的順序)

1.4 并發(fā)(讓人有點(diǎn)懵)

旨在說(shuō)明 “并發(fā)” 的幾種情況摔握,在js中看似并發(fā)實(shí)際是由單線程事件循環(huán)機(jī)制實(shí)現(xiàn)的
均以?xú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)


image.png

回調(diào)

回調(diào)函數(shù)是javascript異步的基本單元

這章主要討論回調(diào)這種自javascript誕生以來(lái)就存在的異步方式存在什么問(wèn)題(思維上的(大腦搞不定)、寫(xiě)法上的(嵌套团搞、硬編碼)严望、信任問(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í)行复隆,代碼寫(xiě)法上很容易形成callback hell拨匆,但這不是重點(diǎn),即使把嵌套寫(xiě)法解開(kāi)寫(xiě)成多個(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ù)編寫(xiě)蜓竹、難以復(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)的代碼;條件滿(mǎn)足后酝蜒,運(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引擎自己控制的0 0 @TODO 了解具體實(shí)現(xiàn)方式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末霉咨,一起剝皮案震驚了整個(gè)濱河市蛙紫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌躯护,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丽涩,死亡現(xiàn)場(chǎng)離奇詭異棺滞,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)矢渊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)继准,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人矮男,你說(shuō)我怎么就攤上這事移必。” “怎么了毡鉴?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵崔泵,是天一觀的道長(zhǎng)秒赤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)憎瘸,這世上最難降的妖魔是什么入篮? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮幌甘,結(jié)果婚禮上潮售,老公的妹妹穿的比我還像新娘。我一直安慰自己锅风,他們只是感情好酥诽,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著皱埠,像睡著了一般肮帐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上漱逸,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天泪姨,我揣著相機(jī)與錄音,去河邊找鬼饰抒。 笑死肮砾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的袋坑。 我是一名探鬼主播仗处,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼枣宫!你這毒婦竟也來(lái)了婆誓?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤也颤,失蹤者是張志新(化名)和其女友劉穎洋幻,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體翅娶,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡文留,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了竭沫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片燥翅。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蜕提,靈堂內(nèi)的尸體忽然破棺而出森书,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布凛膏,位于F島的核電站杨名,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏译柏。R本人自食惡果不足惜镣煮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鄙麦。 院中可真熱鬧典唇,春花似錦、人聲如沸胯府。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)骂因。三九已至炎咖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寒波,已是汗流浹背乘盼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留俄烁,地道東北人绸栅。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像页屠,于是被迫代替她去往敵國(guó)和親粹胯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容