JavaScript執(zhí)行機制以及異步解決方案的發(fā)展歷程

瀏覽器是多進程也是多線程的

??每打開一個瀏覽器頁面就相當(dāng)于創(chuàng)建了一個獨立的瀏覽器進程桐款。瀏覽器的線程包括:

??1.GUI渲染線程

??負(fù)責(zé)渲染瀏覽器界面,解析HTML盏混、CSS,構(gòu)建DOM樹和RenderObject樹续搀,布局和繪制等帐我。當(dāng)界面需要回流坎炼、重繪時,該線程就會執(zhí)行拦键。 注意:GUI渲染線程與JS引擎線程是互斥的谣光,當(dāng)JS引擎執(zhí)行時GUI線程會被掛起。

??2.JS引擎線程

??也稱為JS內(nèi)核芬为,負(fù)責(zé)處理JavaScript腳本程序(例如V8引擎)萄金,同樣注意,GUI渲染線程與JS引擎線程是互斥的媚朦,如果JS執(zhí)行時間過長氧敢,就會造成頁面渲染阻塞,這也是為什么JS腳本寫在HTML询张、CSS下面的原因孙乖。

??3.事件觸發(fā)線程

??用于將鼠標(biāo)點擊事件等添加到任務(wù)隊列中,運行并返回結(jié)果份氧,等待JS引擎處理唯袄。

??4.定時器觸發(fā)線程

??用于將定時器任務(wù)添加到任務(wù)隊列中,運行并返回結(jié)果蜗帜,等待JS引擎處理恋拷。

??5.異步http請求線程

??用于將異步請求添加到任務(wù)隊列中,運行并返回結(jié)果厅缺,等待JS引擎處理蔬顾。
總結(jié):js引擎線程就是主線程,3湘捎、4诀豁、5線程是子線程,HTML5支持的Web Worker就是處理任務(wù)隊列中的程序消痛,也屬于子線程且叁。

load事件與DOMContentLoaded事件的先后

??DOMContentLoaded事件觸發(fā)時,僅當(dāng)DOM加載完成秩伞,不包括樣式逞带、圖片。當(dāng)onload事件觸發(fā)時纱新,頁面上所有的DOM展氓、樣式、圖片脸爱、腳本都已經(jīng)加載完成遇汞。

CSS加載是否會阻塞DOM樹渲染

??css是由單獨的下載線程異步下載的,不會阻塞dom樹解析,所以放在頭部引入空入,不過會阻塞render樹渲染络它,因為渲染需要等css加載完畢。

JavaScript執(zhí)行機制

??JavaScript語言的一大特點就是單線程歪赢,也就是說化戳,一個時間段內(nèi)只能做一件事情。為什么JavaScript不能有多個線程呢埋凯?這樣能提高效率点楼。假定JavaScript同時有兩個線程,一個線程在某個DOM節(jié)點上添加內(nèi)容白对,另一個線程刪除了這個節(jié)點掠廓,這兩個線程就會沖突?所以為了避免這個問題甩恼,從一誕生蟀瞧,JavaScript就是單線程。這已經(jīng)成了這門語言的核心特征媳拴,將來也不會變黄橘。
??單線程意味著所有任務(wù)都需要排隊,前一個任務(wù)結(jié)束才會執(zhí)行后一個任務(wù)屈溉。如果前一個任務(wù)耗時很長,后一個任務(wù)就不得不等待抬探,(造成IO設(shè)備很慢(比如AJAX請求)子巾,那么CPU不得不等著結(jié)果出來再往下執(zhí)行,)導(dǎo)致頁面渲染阻塞小压,用戶體驗不好线梗。于是,所有任務(wù)可以分成兩種怠益,一種是同步任務(wù)仪搔,另一種是異步任務(wù)。同步任務(wù)指的是蜻牢,在主線程上排隊執(zhí)行的任務(wù)烤咧,前一個任務(wù)執(zhí)行完畢,就會執(zhí)行后一個任務(wù)抢呆;異步任務(wù)指的是煮嫌,不進入主線程、而進入"任務(wù)隊列"的任務(wù)抱虐,等到主線程空了昌阿,就會讀取"任務(wù)隊列",有運行結(jié)果的異步任務(wù)就會被推入到主線程中執(zhí)行,主線程會不斷重復(fù)這個過程懦冰。這就是JavaScript的運行機制灶轰,又稱為Event Loop(事件循環(huán))。

JS 異步解決方案的發(fā)展歷程以及優(yōu)缺點刷钢。

  1. 回調(diào)函數(shù)(callback)
setTimeout(() => {
   // callback 函數(shù)體
}, 1000)

缺點:回調(diào)地獄笋颤,不能用 try catch 捕獲錯誤,不能 return
回調(diào)地獄的根本問題在于:
缺乏順序性: 回調(diào)地獄導(dǎo)致的調(diào)試?yán)щy闯捎,和大腦的思維方式不符椰弊;
嵌套函數(shù)存在耦合性,一旦有所改動瓤鼻,就會牽一發(fā)而動全身秉版,即(控制反轉(zhuǎn));
嵌套函數(shù)過多的多話茬祷,很難處理錯誤清焕。

ajax('XXX1', () => {
   // callback 函數(shù)體
   ajax('XXX2', () => {
       // callback 函數(shù)體
       ajax('XXX3', () => {
           // callback 函數(shù)體
       })
   })
})

優(yōu)點:解決了同步的問題(只要有一個任務(wù)耗時很長,后面的任務(wù)都必須排隊等著祭犯,會拖延整個程序的執(zhí)行)秸妥。

  1. Promise
    Promise 就是為了解決 callback 的問題而產(chǎn)生的。
    Promise 實現(xiàn)了鏈?zhǔn)秸{(diào)用沃粗,也就是說每次 then 后返回的都是一個全新 Promise粥惧,如果我們在 then 中 return ,return 的結(jié)果會被 Promise.resolve() 包裝最盅。
    優(yōu)點:解決了回調(diào)地獄的問題突雪。
ajax('XXX1')
 .then(res => {
     // 操作邏輯
     return ajax('XXX2')
 }).then(res => {
     // 操作邏輯
     return ajax('XXX3')
 }).then(res => {
     // 操作邏輯
 })

缺點:無法取消 Promise ,錯誤需要通過回調(diào)函數(shù)來捕獲涡贱。

  1. Generator
    優(yōu)點:可以控制異步的開始和暫停咏删。
    缺點:必須配合 co 自執(zhí)行器使用。
function *fetch() {
   yield ajax('XXX1', () => {})
   yield ajax('XXX2', () => {})
   yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()
  1. Async/await
    async问词、await 是異步的終極解決方案督函。
    優(yōu)點是:代碼清晰,不用像 Promise 寫一大堆 then 鏈激挪,處理了回調(diào)地獄的問題辰狡;
    缺點:await 將異步代碼改造成同步代碼,如果多個異步操作沒有依賴性而使用 await 會導(dǎo)致性能上的降低灌灾。
async function test() {
 // 以下代碼沒有依賴性的話搓译,完全可以使用 Promise.all 的方式
 // 如果有依賴性的話,其實就是解決回調(diào)地獄的例子了
 await fetch('XXX1')
 await fetch('XXX2')
 await fetch('XXX3')
}

下面來看一個使用 await 的例子:

let a = 0
let b = async () => {
 a = a + await 10
 console.log('2', a) // -> '2' 10
}
b()
a++
console.log('1', a) // -> '1' 1

對于以上代碼你可能會有疑惑锋喜,讓我來解釋下原因:

首先函數(shù) b 先執(zhí)行些己,在執(zhí)行到 await 10 之前變量 a 還是 0豌鸡,因為 await 內(nèi)部實現(xiàn)了generator ,generator 會保留堆棧中東西段标,所以這時候 a = 0 被保存了下來涯冠;

因為 await 是異步操作,后來的表達式不返回 Promise 的話逼庞,就會包裝成Promise.reslove(返回值)蛇更,然后會去執(zhí)行函數(shù)外的同步代碼;

同步代碼執(zhí)行完畢后開始執(zhí)行異步代碼赛糟,將保存下來的值拿出來使用派任,這時候 a = 0 + 10。
上述解釋中提到了 await 內(nèi)部實現(xiàn)了 generator璧南,其實 await 就是 generator 加上Promise的語法糖掌逛,且內(nèi)部實現(xiàn)了自動執(zhí)行 generator。如果你熟悉 co 的話司倚,其實自己就可以實現(xiàn)這樣的語法糖豆混。
本題鏈接:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/11

try...catch

try...catch是被設(shè)計成捕獲當(dāng)前執(zhí)行環(huán)境的異常,意思是只能捕獲同步代碼里面的異常动知,異步調(diào)用里面的異常無法捕獲皿伺。
第一種方式:異常出現(xiàn)在異步調(diào)用里面,try..catch無法捕獲盒粮。
第二重方式:try..catch是寫在異步代碼里面鸵鸥,相對于try里面的所有執(zhí)行都是同步代碼,所以能捕獲丹皱。

定時器

??除了放置異步任務(wù)的事件脂男,"任務(wù)隊列"還可以放置定時器,它在"任務(wù)隊列"的尾部添加一個事件种呐,因此要等到同步任務(wù)和"任務(wù)隊列"現(xiàn)有的事件都處理完,才會得到執(zhí)行弃甥。

回調(diào)函數(shù)的使用場合

資源加載:動態(tài)加載js文件后執(zhí)行回調(diào)爽室,加載 iframe后執(zhí)行回調(diào),ajax操作回調(diào)淆攻,圖片加載完成執(zhí)行回調(diào)阔墩。

事件循環(huán)進階:macrotask與microtask

??macrotask(task):宏任務(wù),可以理解是每次執(zhí)行棧執(zhí)行的代碼就是一個宏任務(wù)(包括每次從事件隊列中獲取一個事件回調(diào)并放到執(zhí)行棧中執(zhí)行)瓶珊,包括主代碼塊(同步)啸箫、setTimeout、setInterval等伞芹。
??microtask(jobs):微任務(wù)忘苛,在task執(zhí)行結(jié)束后立即執(zhí)行的任務(wù)蝉娜,包括promise的then、process.nextTick(微任務(wù)隊列扎唾,優(yōu)先級更高)等

優(yōu)先級:同步 > 微任務(wù)(異步) > 宏任務(wù)(異步)
優(yōu)先級:process.nextTick > promise
執(zhí)行過程:macrotask > microtask > 渲染 > (下一輪)macrotask > microtask > 渲染...
注釋:1.線程可以理解為做事召川,一個線程表示一個時間段只能做一件事,多個線程表示一個時間段可以同時做多件事胸遇。
2.CPU:中央處理器是一塊超大規(guī)模的集成電路荧呐,是一臺計算機的運算核心和控制核心。它的功能主要是解釋計算機指令以及處理計算機軟件中的數(shù)據(jù)纸镊。
3.I/O設(shè)備是指輸入/輸出設(shè)備接口倍阐。

http://www.reibang.com/p/334287bdbdd0

參考網(wǎng)址:
http://www.ruanyifeng.com/blog/2014/10/event-loop.html(js執(zhí)行機制)
https://www.cnblogs.com/cangqinglang/p/8963557.html(js執(zhí)行機制)
https://zhuanlan.zhihu.com/p/43282197(css加載會造成阻塞嗎)
https://blog.csdn.net/liwenfei123/article/details/80670330(宏任務(wù)和微任務(wù))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市逗威,隨后出現(xiàn)的幾起案子峰搪,更是在濱河造成了極大的恐慌,老刑警劉巖庵楷,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罢艾,死亡現(xiàn)場離奇詭異,居然都是意外死亡尽纽,警方通過查閱死者的電腦和手機咐蚯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弄贿,“玉大人春锋,你說我怎么就攤上這事〔畎迹” “怎么了期奔?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長危尿。 經(jīng)常有香客問我呐萌,道長,這世上最難降的妖魔是什么谊娇? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任肺孤,我火速辦了婚禮,結(jié)果婚禮上济欢,老公的妹妹穿的比我還像新娘赠堵。我一直安慰自己,他們只是感情好法褥,可當(dāng)我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布茫叭。 她就那樣靜靜地躺著,像睡著了一般半等。 火紅的嫁衣襯著肌膚如雪揍愁。 梳的紋絲不亂的頭發(fā)上呐萨,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天,我揣著相機與錄音吗垮,去河邊找鬼垛吗。 笑死,一個胖子當(dāng)著我的面吹牛烁登,可吹牛的內(nèi)容都是我干的怯屉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼饵沧,長吁一口氣:“原來是場噩夢啊……” “哼锨络!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起狼牺,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤羡儿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后是钥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掠归,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年悄泥,在試婚紗的時候發(fā)現(xiàn)自己被綠了虏冻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡弹囚,死狀恐怖厨相,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鸥鹉,我是刑警寧澤蛮穿,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站毁渗,受9級特大地震影響践磅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灸异,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一音诈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绎狭,春花似錦、人聲如沸褥傍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恍风。三九已至蹦狂,卻和暖如春誓篱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凯楔。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工窜骄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人摆屯。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓邻遏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親虐骑。 傳聞我的和親對象是個殘疾皇子准验,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,647評論 2 354