摘要:同步異步是單線程的,每次只能做一件事情。像以下這種情況,代碼會(huì)按順序執(zhí)行,這個(gè)就叫同步。雖然是單線程,但是瀏覽器是多線程的,在遇到像事件等這種任務(wù)時(shí),會(huì)轉(zhuǎn)交給瀏覽器的其他工作線程上面提到的幾個(gè)線程執(zhí)行,執(zhí)行完之后將回調(diào)函數(shù)放入到任務(wù)隊(duì)列。
同步、異步
JS是單線程的,每次只能做一件事情。像以下這種情況,代碼會(huì)按順序執(zhí)行,這個(gè)就叫同步。
console.log(1); console.log(2); console.log(3);
以下代碼會(huì)輸出2、3、1,像這種不按順序執(zhí)行的,或者說(shuō)代碼執(zhí)行中間有時(shí)間間隙的,叫異步。
setTimeout(() => { console.log(1); }, 0); console.log(2); console.log(3);事件循環(huán)
一個(gè)瀏覽器通常有以下幾個(gè)常駐的線程:
渲染引擎線程:該線程負(fù)責(zé)頁(yè)面的渲染
JS引擎線程:負(fù)責(zé)JS的解析和執(zhí)行
定時(shí)觸發(fā)器線程:處理定時(shí)事件,比如setTimeout, setInterval
事件觸發(fā)線程:處理DOM事件
異步http請(qǐng)求線程:處理http請(qǐng)求
渲染線程和JS引擎線程是不能同時(shí)進(jìn)行的。也就是說(shuō)在執(zhí)行代碼時(shí),渲染會(huì)掛起;渲染DOM時(shí),代碼也不會(huì)執(zhí)行。
雖然JS是單線程,但是瀏覽器是多線程的,在遇到像setTimeout、DOM事件、ajax等這種任務(wù)時(shí),會(huì)轉(zhuǎn)交給瀏覽器的其他工作線程(上面提到的幾個(gè)線程)執(zhí)行,執(zhí)行完之后將回調(diào)函數(shù)放入到任務(wù)隊(duì)列。
在JS運(yùn)行環(huán)境里,除了主線程外,還有任務(wù)隊(duì)列。
// eventLoop是一個(gè)用作隊(duì)列的數(shù)組 // (先進(jìn),先出) var eventLoop = [ ]; var event; // “永遠(yuǎn)”執(zhí)行 while (true) { // 一次tick if (eventLoop.length > 0) { // 拿到隊(duì)列中的下一個(gè)事件 event = eventLoop.shift(); // 現(xiàn)在,執(zhí)行下一個(gè)事件 event(); } }
我們可以用上面的代碼來(lái)想像一下JS的執(zhí)行情況。
JS主線程,就像是一個(gè)while循環(huán),會(huì)一直執(zhí)行下去。在這期間,每次都會(huì)查看任務(wù)隊(duì)列有沒(méi)有需要執(zhí)行的任務(wù)(回調(diào)函數(shù))。在執(zhí)行完一個(gè)任務(wù)之后,會(huì)繼續(xù)下一個(gè)循環(huán),直到任務(wù)隊(duì)列所有任務(wù)都執(zhí)行完為止。
任務(wù)隊(duì)列又分微任務(wù)隊(duì)列和宏任務(wù)隊(duì)列
微任務(wù)Promise
MutationObserver
Object.observe()(已廢棄)
宏任務(wù)setTimeout
setInterval
setImmediate
IO
UI rendering(DOM event)
執(zhí)行過(guò)程在JS執(zhí)行完同步任務(wù)之后,會(huì)開(kāi)始執(zhí)行微任務(wù)隊(duì)列
在將所有的微任務(wù)執(zhí)行完之后,會(huì)開(kāi)始執(zhí)行宏任務(wù)隊(duì)列
在執(zhí)行完一個(gè)宏任務(wù)之后,跳出來(lái),重新開(kāi)始下一個(gè)循環(huán)(從1開(kāi)始執(zhí)行)
也就是說(shuō)執(zhí)行微任務(wù)隊(duì)列 會(huì)將隊(duì)列中的所有微任務(wù)執(zhí)行完 而執(zhí)行宏任務(wù)隊(duì)列 每次只執(zhí)行一個(gè)宏任務(wù) 然后重新開(kāi)始下一個(gè)循環(huán)
我們可以看看以下代碼
setTimeout(() => { console.log(3) new Promise((resolve, reject) => { console.log(5) resolve() }).then(console.log(6)) }, 0) setTimeout(() => { console.log(4) }, 0) new Promise((resolve, reject) => { console.log(1) resolve() }).then(console.log(2))
輸出是1 2 3 5 6 4
我們來(lái)分析一下代碼的執(zhí)行過(guò)程
前面的兩個(gè)setTimeout都是宏任務(wù),所以現(xiàn)在宏任務(wù)隊(duì)列有2個(gè)任務(wù)
Promise里面的代碼是同步任務(wù),所以現(xiàn)在會(huì)馬上執(zhí)行 輸出1
Promise的then是微任務(wù),所以現(xiàn)在微任務(wù)隊(duì)列有1個(gè)任務(wù)
在執(zhí)行完同步任務(wù)之后,開(kāi)始執(zhí)行微任務(wù),也就是console.log(2), 輸出2
在執(zhí)行完微任務(wù)之后,會(huì)執(zhí)行宏任務(wù),第一個(gè)宏任務(wù)也就是第一個(gè)setTimeout
第一個(gè)setTimeout會(huì)先輸出3,然后輸出5,因?yàn)檫@兩個(gè)都是同步任務(wù),然后遇到then,加入微任務(wù)隊(duì)列,宏任務(wù)執(zhí)行完重新開(kāi)始下一個(gè)循環(huán)。
因?yàn)闆](méi)有同步代碼,所以接著執(zhí)行微任務(wù),此時(shí)微任務(wù)隊(duì)列有1個(gè)任務(wù)(第6步加入), 宏任務(wù)隊(duì)列還有1個(gè)任務(wù)(第6步執(zhí)行完了第一個(gè)宏任務(wù))
執(zhí)行微任務(wù),輸出6
再執(zhí)行宏任務(wù),輸出4
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100395.html
摘要:回調(diào)函數(shù),一般在同步情境下是最后執(zhí)行的,而在異步情境下有可能不執(zhí)行,因?yàn)槭录](méi)有被觸發(fā)或者條件不滿足。同步方式請(qǐng)求異步同步請(qǐng)求當(dāng)請(qǐng)求開(kāi)始發(fā)送時(shí),瀏覽器事件線程通知主線程,讓線程發(fā)送數(shù)據(jù)請(qǐng)求,主線程收到 一直以來(lái)都知道JavaScript是一門(mén)單線程語(yǔ)言,在筆試過(guò)程中不斷的遇到一些輸出結(jié)果的問(wèn)題,考量的是對(duì)異步編程掌握情況。一般被問(wèn)到異步的時(shí)候腦子里第一反應(yīng)就是Ajax,setTimse...
摘要:函數(shù)會(huì)在之后的某個(gè)時(shí)刻觸發(fā)事件定時(shí)器。事件循環(huán)中的這樣一次遍歷被稱為一個(gè)。執(zhí)行完畢并出棧。當(dāng)定時(shí)器過(guò)期,宿主環(huán)境會(huì)把回調(diào)函數(shù)添加至事件循環(huán)隊(duì)列中,然后,在未來(lái)的某個(gè)取出并執(zhí)行該事件。 原文請(qǐng)查閱這里,略有改動(dòng)。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第四章。 現(xiàn)在,我們將會(huì)通過(guò)回顧單線程環(huán)境下編程的弊端及如何克服這些困難以創(chuàng)建令人驚嘆...
摘要:事件循環(huán)事件循環(huán)是指主線程重復(fù)從消息隊(duì)列中取消息執(zhí)行的過(guò)程。事件觸發(fā)時(shí),表示異步任務(wù)完成,會(huì)將事件監(jiān)聽(tīng)器函數(shù)封裝成一條消息放到消息隊(duì)列中,等待主線程執(zhí)行。 一. 單線程 我們常說(shuō)JavaScript是單線程的。 所謂單線程,是指在JS引擎中負(fù)責(zé)解釋和執(zhí)行JavaScript代碼的線程只有一個(gè)。不妨叫它主線程。 但是實(shí)際上還存在其他的線程。例如:處理AJAX請(qǐng)求的線程、處理DOM事件的線...
摘要:當(dāng)主線程開(kāi)始執(zhí)行異步任務(wù),實(shí)際就是執(zhí)行對(duì)應(yīng)的回調(diào)函數(shù)。異步任務(wù)必須指定回調(diào)函數(shù)。所以注意的是,只是將事件插入了任務(wù)隊(duì)列,必須等到當(dāng)前代碼執(zhí)行棧執(zhí)行完,主線程才會(huì)去執(zhí)行它指定的回調(diào)函數(shù)。 最近本人對(duì)于js的運(yùn)行機(jī)制,特別是異步,還有回調(diào)函數(shù)感覺(jué)很亂,于是參考了很多有用的博客(博客原文地址會(huì)在文末給出),整理如下: js單線程 我們都知道,Javascript語(yǔ)言的執(zhí)行環(huán)境是單線程(si...
閱讀 1791·2021-11-11 11:02
閱讀 1697·2021-09-22 15:55
閱讀 2499·2021-09-22 15:18
閱讀 3500·2019-08-29 11:26
閱讀 3758·2019-08-26 13:43
閱讀 2656·2019-08-26 13:32
閱讀 912·2019-08-26 10:55
閱讀 973·2019-08-26 10:27