Author:
bugall
Wechat:bugallF
Email:769088641@qq.com
Github: https://github.com/bugallgit
雖然咱們用Javascript老是能夠實現一些異步代碼, 可是Javascript中真正的異步概念,可是直到ES6,Javascript才內建了直接的異步概念。github
對於原有Javascript引擎來講, 它只關心如何去執行給定的代碼塊, 對於何時該執行哪些代碼塊這個是引擎不關心的。引擎是依賴於宿主環境的,這裏的宿主環境並非指操做系統環境,由於不一樣的平臺提供的「可執行環境」不一樣。而宿主環境就是爲了隔離代碼、語言與具體的平臺而提出的一個設計。好比web瀏覽器環境,Node.js這樣的工具等,全部的這些環境都有一個共同點,它們都提供了一種機制來處理程序中多個代碼塊的執行,且執行每塊時調用Javascript引擎,這種機制被成爲事件循環
。web
換句話說,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會被取出執行。code
若是你的事件循環中已經有不少項目後,定時器的回調就要被放到隊尾( 不支持搶佔式 )等待被執行,這也就是定時器不許的緣由。定時器的回調函數的執行要根據時間隊列的狀態而定。
那麼該如何去下降定時器偏差呢?
嚴格來講,定時器並不直接把回調函數直接插到事件循環隊列,定時器會在有機會的時候插入事件,對於連續的兩個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的任務隊列被執行