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