摘要:需要注意的是,定時器只是將事件插入了任務(wù)隊列,必須等到當(dāng)前代碼執(zhí)行棧執(zhí)行完,主線程才會去執(zhí)行它指定的回調(diào)函數(shù)。如果當(dāng)前代碼耗時很長,有可能要等很久,所以并沒有辦法保證,回調(diào)函數(shù)一定會在指定的時間執(zhí)行。這也引申出的并發(fā)模型。
一、JavaScript的單線程
眾所周知,JavaScript的一大特點就是單線程,但是我們有沒有思考過它為什么不能是多線程的?
我們假定JavaScript有兩個線程,一個線程在某個DOM節(jié)點上添加內(nèi)容,另一個線程刪除了這個節(jié)點,這時瀏覽器應(yīng)該以哪個線程為準(zhǔn)?所以為了避免這種復(fù)雜性,從一誕生,JavaScript就是單線程。
盡管HTML5提出Web Worker,允許JavaScript腳本創(chuàng)建多個線程,但是子線程完全受主線程控制,且不得操作DOM。所以,并沒有改變JavaScript單線程的本質(zhì)。
二、定時器定時器主要是setTimeout()和setInterval()這兩個函數(shù),這也是平時編程時候用到最多的。
console.log(1); setTimeout(function() { console.log(2); },5000); console.log(3);
上面代碼的執(zhí)行結(jié)果是1,3,2。但如果將setTimeout()的第二個參數(shù)設(shè)為0,就表示當(dāng)前代碼執(zhí)行完以后,立即執(zhí)行(0毫秒延遲)指定的回調(diào)函數(shù)。setTimeout(fn,0)的含義是,它在任務(wù)隊列的尾部添加一個事件,在主線程最早得到空閑時去執(zhí)行,也就是說,盡可能早得執(zhí)行。
需要注意的是,定時器只是將事件插入了任務(wù)隊列,必須等到當(dāng)前代碼(執(zhí)行棧)執(zhí)行完,主線程才會去執(zhí)行它指定的回調(diào)函數(shù)。如果當(dāng)前代碼耗時很長,有可能要等很久,所以并沒有辦法保證,回調(diào)函數(shù)一定會在setTimeout()指定的時間執(zhí)行。這也引申出JavaScript的并發(fā)模型。
三、并發(fā)模型我們先看一下理論上的并發(fā)模型:
四、Event Loop棧(stack):函數(shù)調(diào)用會形成了一個堆棧幀
堆(heap):對象被分配在一個堆中,一個用以表示一個內(nèi)存中大的未被組織的區(qū)域
隊列(queue):運行時包含的一個待處理的消息隊列。當(dāng)棧為空時,則從隊列中取出一個消息進行處理。這個處理過程包含了調(diào)用與這個消息相關(guān)聯(lián)的函數(shù)(以及因而創(chuàng)建了一個初始堆棧幀)
針對上面的并發(fā)模型和JavaScript的同步異步運行機制,我們可以看到整個流程大致是這樣的:
1.所有同步任務(wù)都在主線程上執(zhí)行,形成一個執(zhí)行棧(并發(fā)模型的stack)。
2.主線程之外,還存在一個任務(wù)隊列(并發(fā)模型的queue)。只要異步任務(wù)有了運行結(jié)果,就在任務(wù)隊列中放置一個事件。
3.一旦執(zhí)行棧中的所有同步任務(wù)執(zhí)行完畢,系統(tǒng)就會讀取任務(wù)隊列,看看里面有哪些事件和那些對應(yīng)的異步任務(wù),于是等待結(jié)束狀態(tài),進入執(zhí)行棧,開始執(zhí)行。
4.主線程不斷重復(fù)上面的第三步。
這個過程是循環(huán)不斷的,所以這種運行機制又稱為Event Loop(事件循環(huán))。放一張大神演講時的圖片來更好地理解Event Loop:
我們可以看到,主線程運行的時候,產(chǎn)生堆(heap)和棧(stack),棧中的代碼調(diào)用各種外部API,它們在任務(wù)隊列中加入各種事件(click,load,done)。只要棧中的代碼執(zhí)行完畢,主線程就會去讀取任務(wù)隊列,依次執(zhí)行那些事件所對應(yīng)的回調(diào)函數(shù)。
五、Macrotask 和 Microtask這是一個比較冷門的知識,在并發(fā)模型中隊列又可以分為Macrotask 和 Microtask,它們都屬于異步任務(wù)。先來看一個例子:
console.log("script start"); setTimeout(function() { console.log("setTimeout"); }, 0); Promise.resolve().then(function() { console.log("promise1"); setTimeout(function() { console.log("setTimeout in microtask"); }, 0); }).then(function() { console.log("promise2"); }); console.log("script end");
輸出:
script start
script end
promise1
promise2
setTimeout
setTimeout in microtask
Macrotask 和 Microtask有什么區(qū)別呢?
Macrotasks:setTimeout, setInterval, setImmediate, I/O, UI rendering
Microtask:process.nextTick, Promises, Object.observe(廢棄), MutationObserver
它們的執(zhí)行過程如下:
JavaScript引擎首先從macrotask queue中取出第一個任務(wù)
執(zhí)行完畢后,將microtask queue中的所有任務(wù)取出,按順序全部執(zhí)行
然后再從macrotask queue中取下一個
執(zhí)行完畢后,再次將microtask queue中的全部取出
循環(huán)往復(fù),直到兩個queue中的任務(wù)都取完
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://hztianpu.com/yun/88472.html
摘要:而函數(shù)調(diào)用結(jié)束返回時,運行時會將棧頂?shù)恼{(diào)用結(jié)構(gòu)彈出。并發(fā)模型與引擎是單線程的,它的并發(fā)模型基于事件循環(huán)當(dāng)線程中的同步任務(wù)執(zhí)行完,執(zhí)行棧為空時,則從任務(wù)隊列中取出異步任務(wù)進行處理。在當(dāng)前的微任務(wù)沒有執(zhí)行完成時,是不會執(zhí)行下一個宏任務(wù)的。 堆/棧/隊列 在javascript中,存在調(diào)用棧 (call stack)和內(nèi)存堆(memory heap) ,程序中函數(shù)依次進入棧中等待執(zhí)行,若執(zhí)行...
摘要:若以多線程的方式操作這些,則可能出現(xiàn)操作的沖突。另外,因為是單線程的,在某一時刻內(nèi)只能執(zhí)行特定的一個任務(wù),并且會阻塞其它任務(wù)執(zhí)行。瀏覽器事件觸發(fā)線程事件觸發(fā)線程,當(dāng)一個事件被觸發(fā)時該線程會把事件添加到任務(wù)隊列的隊尾,等待引擎的處理。 首先,說下為什么 JavaScript 是單線程? 總所周知,JavaScript是以單線程的方式運行的。說到線程就自然聯(lián)想到進程。那它們有什么聯(lián)系呢? ...
摘要:如果當(dāng)前沒有事件也沒有定時器事件,則返回。相關(guān)資料關(guān)于的架構(gòu)及設(shè)計思路的事件討論了使用線程池異步運行代碼。下一篇初窺事件機制的實現(xiàn)二中定時器的實現(xiàn) 在瀏覽器中,事件作為一個極為重要的機制,給予JavaScript響應(yīng)用戶操作與DOM變化的能力;在Node.js中,事件驅(qū)動模型則是其高并發(fā)能力的基礎(chǔ)。 學(xué)習(xí)JavaScript也需要了解它的運行平臺,為了更好的理解JavaScript的事...
摘要:標(biāo)簽單線程首發(fā)地址碼農(nóng)網(wǎng)細說單線程的一些事最近被同學(xué)問道單線程的一些事,我竟回答不上。若以多線程的方式操作這些,則可能出現(xiàn)操作的沖突。另外,因為是單線程的,在某一時刻內(nèi)只能執(zhí)行特定的一個任務(wù),并且會阻塞其它任務(wù)執(zhí)行。 標(biāo)簽: JavaScript 單線程 首發(fā)地址:碼農(nóng)網(wǎng)《細說JavaScript單線程的一些事》 最近被同學(xué)問道 JavaScript 單線程的一些事,我竟回答不上。好...
摘要:標(biāo)簽單線程首發(fā)地址碼農(nóng)網(wǎng)細說單線程的一些事最近被同學(xué)問道單線程的一些事,我竟回答不上。若以多線程的方式操作這些,則可能出現(xiàn)操作的沖突。另外,因為是單線程的,在某一時刻內(nèi)只能執(zhí)行特定的一個任務(wù),并且會阻塞其它任務(wù)執(zhí)行。 標(biāo)簽: JavaScript 單線程 首發(fā)地址:碼農(nóng)網(wǎng)《細說JavaScript單線程的一些事》 最近被同學(xué)問道 JavaScript 單線程的一些事,我竟回答不上。好...
閱讀 1745·2021-10-14 09:43
閱讀 6015·2021-09-07 10:21
閱讀 1480·2019-08-30 15:56
閱讀 2269·2019-08-30 15:53
閱讀 1344·2019-08-30 15:44
閱讀 2132·2019-08-30 15:44
閱讀 1450·2019-08-29 17:24
閱讀 880·2019-08-29 15:19