摘要:需要注意的是,定時器比較特殊,并沒有把回調函數掛在事件循環隊列中,它所做的就是設置一個定時器,當定時器到時后,環境會把你的回調函數放在事件循環中,這樣,在未來某個時刻的會被取出執行。
一: 事件循環Author: bugall
Wechat: bugallF
Email: 769088641@qq.com
Github: https://github.com/bugall
雖然我們用Javascript總是可以實現一些異步代碼, 但是Javascript中真正的異步概念,但是直到ES6,Javascript才內建了直接的異步概念。
對于原有Javascript引擎來說, 它只關心如何去執行給定的代碼塊, 對于什么時候該執行哪些代碼塊這個是引擎不關心的。引擎是依賴于宿主環境的,這里的宿主環境并不是指操作系統環境,因為不同的平臺提供的“可執行環境”不同。而宿主環境就是為了隔離代碼、語言與具體的平臺而提出的一個設計。比如web瀏覽器環境,Node.js這樣的工具等,所有的這些環境都有一個共同點,它們都提供了一種機制來處理程序中多個代碼塊的執行,且執行每塊時調用Javascript引擎,這種機制被成為事件循環。
換句話說,Javascript引擎本身并沒有時間的概念,只是一個按需執行Javascript任意代碼片段的環境。“事件”的調度總是由包含它的環境進行。
注意!!!,ES6后事件的管理方式有所改變。ES6本身解決的事件在哪里管理的問題,現在ES6精確指定了事件循環的工作細節,這就 意味著技術上將其納如了Javascript引擎的勢力范圍,而不再由宿主環境管理,這個改變的一個主要原因是ES6中Promise的引入,這個技術要求事件循環隊列的調度運行能夠直接進行精細的控制
我們看下面的這段代碼:
function task() { console.log("Hello Word"); } setTimeout(task, 1000);
如果你在代碼中設置一個計時器, 當計時器到達指定的時間后執行函數task, 當Javascript引擎執行到定時器的時候會通知宿主環境:“我要去做別的事情, 等1秒后就調用task函數,注意:是調用而不是執行)
我們用一段偽代碼實現一個簡單的事件循環
var eventList = [event1, event2, event3]; var event; while(true) { if (eventList.length > 0 ) { event = eventList.pop(); } run(event); }
可以看到有一個用while循環實現的持續運行的循環,當event1, event2, event3都取出被執行一次后稱為一輪,循環的每一輪稱為一個tick,對于每一個tick而言,如果在隊列中有等待的事件,那么就會從隊列中取出下一個事件并執行,這些事件就是我們代碼中寫的回調函數。
需要注意的是,定時器比較特殊,setTimeout(task, 1000)并沒有把回調函數掛在事件循環隊列中,它所做的就是設置一個定時器,當定時器到時后,環境會把你的回調函數放在事件循環中,這樣,在未來某個時刻的tick會被取出執行。
如果你的事件循環中已經有很多項目后,定時器的回調就要被放到隊尾( 不支持搶占式 )等待被執行,這也就是定時器不準的原因。定時器的回調函數的執行要根據時間隊列的狀態而定。
那么該如何去降低定時器誤差呢?
嚴格來說,定時器并不直接把回調函數直接插到事件循環隊列,定時器會在有機會的時候插入事件,對于連續的兩個setTimeout(..., 0)調用不能保證會嚴格按照調用順序處理,所以各種情況都會發生,比如定時器漂移,這類結果是不可預測的,在Node.js中可以用process.nextTick(...)。但是不能保證所有環境都能控制異步的順序。
在ES6中,在事件循環隊列上有個一個新的概念,那就是任務隊列,這個概念給大家帶來的最大影響可能就是Promise的異步特性
任務隊列就是掛在時間循環隊列的每個tick之后的一個隊列,在事件循環的每個tick中,可能出現的異步動作不會導致一個完整的新事件添加到事件循環隊列中,而會在當前tick的任務隊列末尾添加一個任務。
一個任務可能引起更多任務被添加到同一個隊列末尾,所以理論上說,任務循環可能無限循環,無法轉移到下一個事件循環tick.
任務隊列的概念,那么怎么減少定時器的誤差?看代碼:
function task() { console.log("Hello Word"); } setTimeout(task, 1000);
如果現在時間隊列中有100個等待被執行的任務,這時候task任務準備插入到事件隊列。
沒有引入任務隊列前:
task會被插入到當前事件循環隊列的末端,等待下次的tick被執行,那么這就需要等到當前的tick被執行完,那么這時候的timer延時就決定于100個等待執行的任務耗時。
引入任務隊列之后:
直接插入到當前tick的任務隊列被執行
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86986.html
摘要:主線程不斷重復上面的三步,此過程也就是常說的事件循環。所以主線程代碼執行時間過長,會阻塞事件循環的執行。參考資料這一次,徹底弄懂執行機制任務隊列的順序機制事件循環搞懂異步事件輪詢與中的事件循環 1. 說明 讀過本文章后,您能知道: JavaScript代碼在瀏覽器中的執行機制和事件循環 面試中經常遇到的代碼輸出順序問題 首先通過一段代碼來驗證你是否了解代碼輸出順序,如果你不知道輸出...
摘要:而這些隊列由的事件循環來搞定宏任務與微任務,在最新標準中,它們被分別稱為與。我們梳理一下事件循環的執行機制循環首先從宏任務開始,遇到,生成執行上下文,開始進入執行棧,可執行代碼入棧,依次執行代碼,調用完成出棧。 寫在前面 js是一門單線程的編程語言,也就是說js在處理任務的時候,所有任務只能在一個線程上排隊被執行,那如果某一個任務耗時比較長呢?總不能等到它執行結束再去執行下一個。所以在...
摘要:單線程異步非阻塞然后,這又牽扯到了事件循環消息隊列,還有微任務宏任務這些。此步的位置不確定某個時刻后,定時器觸發線程通知事件觸發線程,事件觸發線程將回調函數加入消息隊列隊尾,等待引擎線程執行。 前言 Philip Roberts 在演講 great talk at JSConf on the event loop 中說:要是用一句話來形容 JavaScript,我可能會這樣: Java...
摘要:概述本篇主要介紹的運行機制單線程事件循環結論先在中利用運行至完成和非阻塞完成單線程下異步任務的處理就是先處理主模塊主線程上的同步任務再處理異步任務異步任務使用事件循環機制完成調度涉及的內容有單線程事件循環同步執行異步執行定時器的事件循環開始 1.概述 本篇主要介紹JavaScript的運行機制:單線程事件循環(Event Loop). 結論先: 在JavaScript中, 利用運行至...
摘要:如果沒有其他異步任務要處理比如到期的定時器,會一直停留在這個階段,等待請求返回結果。執行的執行事件關閉請求的,例如事件循環的每一次循環都需要依次經過上述的階段。因此,才會早于執行。 showImg(https://segmentfault.com/img/bVbnY76); 概念 同步任務(Synchronous) 在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務 ...
閱讀 2061·2021-11-23 09:51
閱讀 2203·2021-09-29 09:34
閱讀 3694·2021-09-22 15:50
閱讀 3556·2021-09-22 15:23
閱讀 2559·2019-08-30 15:55
閱讀 699·2019-08-30 15:53
閱讀 3066·2019-08-29 17:09
閱讀 2624·2019-08-29 13:57