js異步歷史
一個 JavaScript 引擎會常駐于內存中,它等待著我們把JavaScript 代碼或者函數傳遞給它執行
在 ES3 和更早的版本中,JavaScript 本身還沒有異步執行代碼的能力,引擎就把代碼直接順次執行了,異步任務都是宿主環境(瀏覽器)發起的(setTimeout、AJAX等)。
在 ES5 之后,JavaScript 引入了 Promise,這樣,不需要瀏覽器的安排,JavaScript 引擎本身也可以發起任務了
JS異步實現原理js為單線程,js引擎中負責解析執行js代碼的線程只有一個(主線程),即每次只能做一件事,其他IO操作放入任務隊列等待執行,異步過程中,工作線程在異步操作完成后需要通知主線程。那么這個通知機制是利用消息隊列和事件循環(EventLoop)實際上,主線程只會做一件事情,就是從消息隊列里面取消息、執行消息,再取消息、再執行。當消息隊列為空時,就會等待直到消息隊列變成非空。而且主線程只有在將當前的消息執行完成后,才會去取下一個消息
node:node.js單線程只是一個js主線程,本質上的異步操作還是由線程池完成的,node將所有的阻塞操作都交給了內部的線程池去實現,本身只負責不斷的往返調度,并沒有進行真正的I/O操作,從而實現異步非阻塞I/O,這便是node單線程的精髓之處了。
消息隊列:消息隊列是一個先進先出的隊列,它里面存放著各種消息。
事件循環:事件循環是指主線程重復從消息隊列中取消息、執行的過程。(瀏覽器至少有一個事件循環,一個事件循環至少有一個任務隊列(macrotask))
微任務:
JavaScript 引擎發起的任務 - JS 引擎級別
promise回調,MutationObserver,process.nextTick,Object.observe
宏任務
宿主發起的任務,每次的一段js代碼執行過程,其實都是一個宏觀任務 - 宿主級別
整體的js代碼,事件回調,XHR回調,定時器(setTimeout/setInterval/setImmediate),IO操作,UI render
宏任務和微任務關系:每個macro宏任務會維護一個micro微任務列表
事件循環過程首先我們分析有多少個宏任務;
在每個宏任務中,分析有多少個微任務;
根據調用次序,確定宏任務中的微任務執行次序;
根據宏任務的觸發規則和調用次序,確定宏任務的執行次序;
確定整個順序
視圖渲染時機:本輪事件循環的microtask隊列被執行完之后(不是每輪事件循環都會執行視圖更新,瀏覽器有自己的優化策略)
注意:執行任務的耗時會影響視圖渲染的時機。通常瀏覽器以每秒60幀(60fps)的速率刷新頁面(16.7ms渲染一幀)所以如果要讓用戶覺得順暢,單個macrotask及它相關的所有microtask最好能在16.7ms內完成。
Node 概念非阻塞 I/O 操作:盡管 JavaScript 是單線程處理的——當有可能的時候,它們會把操作轉移到系統內核中去,當其中的一個操作完成的時候,內核通知 Node.js 將適合的回調函數添加到 輪詢 隊列中等待時機執行
事件循環過程
過程
event loop 的每個階段都有一個任務隊列(一個 FIFO 隊列來執行回調)
當 event loop 到達某個階段時,將執行該階段的任務隊列,直到隊列清空或執行的回調達到系統上限后,才會轉入下一個階段
當所有階段被順序執行一次后,稱 event loop 完成了一個 tick
每次事件循環都包含了6個階段
timers 階段:這個階段執行timer(setTimeout、setInterval)的回調
I/O callbacks 階段:執行一些系統調用錯誤,比如網絡通信的錯誤回調
idle, prepare 階段:僅node內部使用
poll 階段:獲取新的I/O事件, 適當的條件下node將阻塞在這里
check 階段:執行 setImmediate() 的回調
close callbacks 階段:執行 socket 的 close 事件回調
timers 階段
Node 會去檢查有無已過期的timer,如果有則把它的回調壓入timer的任務隊列中等待執行
技術上來說,poll 階段控制 timers 什么時候執行。
poll 階段
poll 階段主要有2個功能:
處理 poll 隊列的事件
當有已超時的 timer,執行它的回調函數
執行過程:當event loop進入 poll 階段,并且 沒有設定的timers(there are no timers scheduled),會發生下面兩件事之一:
如果 poll 隊列不空,event loop會遍歷隊列并同步執行回調,直到隊列清空或執行的回調數到達系統上限;
如果 poll 隊列為空,則發生以下兩件事之一:
如果代碼已經被setImmediate()設定了回調, event loop將結束 poll 階段進入 check 階段來執行 check 隊列(里的回調)。
如果代碼沒有被setImmediate()設定回調,event loop將阻塞在該階段等待回調被加入 poll 隊列,并立即執行。
當event loop進入 poll 階段,并且 有設定的timers,一旦 poll 隊列為空(poll 階段空閑狀態): event loop將檢查timers,如果有1個或多個timers的下限時間已經到達,event loop將繞回 timers 階段,并執行 timer隊列。
注意:沒有setImmediate()會導致event loop阻塞在poll階段,這樣之前設置的timer豈不是執行不了了?所以咧,在poll階段event loop會有一個檢查機制,檢查timer隊列是否為空,如果timer隊列非空,event loop就開始下一輪事件循環,即重新進入到timer階段。
process.nextTick() VS setImmediate()
process.nextTick()
在各個事件階段之間執行,一旦執行,要直到nextTick隊列被清空,才會進入到下一個事件階段
遞歸調用 process.nextTick(),會導致出現I/O starving(饑餓)
setImmediate
對比
setTimeout(()=>{ console.log("timer1") Promise.resolve().then(function() { console.log("promise1") }) }, 0) setTimeout(()=>{ console.log("timer2") Promise.resolve().then(function() { console.log("promise2") }) }, 0) //瀏覽器: timer1 promise1 timer2 promise2 // node timer1 timer2 promise1 promise2
http://lynnelv.github.io/img/...
http://lynnelv.github.io/img/...
補充閱讀node單線程底層實現機制:
https://juejin.im/post/5b61d8...
https://yq.aliyun.com/article...
https://juejin.im/post/5b1e55...
node setTimeOut(), setInterval(), setImmediate() 以及 process.nextTick()區別
js 三種定時器的區別
https://www.cnblogs.com/onepi...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/103765.html
摘要:如果沒有其他異步任務要處理比如到期的定時器,會一直停留在這個階段,等待請求返回結果。執行的執行事件關閉請求的,例如事件循環的每一次循環都需要依次經過上述的階段。因此,才會早于執行。 showImg(https://segmentfault.com/img/bVbnY76); 概念 同步任務(Synchronous) 在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務 ...
摘要:單線程異步非阻塞然后,這又牽扯到了事件循環消息隊列,還有微任務宏任務這些。此步的位置不確定某個時刻后,定時器觸發線程通知事件觸發線程,事件觸發線程將回調函數加入消息隊列隊尾,等待引擎線程執行。 前言 Philip Roberts 在演講 great talk at JSConf on the event loop 中說:要是用一句話來形容 JavaScript,我可能會這樣: Java...
摘要:主線程要明確的一點是,主線程跟執行棧是不同概念,主線程規定現在執行執行棧中的哪個事件。主線程循環即主線程會不停的從執行棧中讀取事件,會執行完所有棧中的同步代碼。以上參考資料詳解中的事件循環機制中的事件循環運行機制詳解再談 showImg(https://segmentfault.com/img/remote/1460000015317437?w=1920&h=1080); 前言 大家都...
摘要:主線程不斷重復上面的三步,此過程也就是常說的事件循環。所以主線程代碼執行時間過長,會阻塞事件循環的執行。參考資料這一次,徹底弄懂執行機制任務隊列的順序機制事件循環搞懂異步事件輪詢與中的事件循環 1. 說明 讀過本文章后,您能知道: JavaScript代碼在瀏覽器中的執行機制和事件循環 面試中經常遇到的代碼輸出順序問題 首先通過一段代碼來驗證你是否了解代碼輸出順序,如果你不知道輸出...
摘要:上代碼代碼可以看出,不僅函數比指定的回調函數先執行,而且函數也比先執行。這是因為后一個事件進入的時候,事件環可能處于不同的階段導致結果的不確定。這是因為因為執行完后,程序設定了和,因此階段不會被阻塞進而進入階段先執行,后進入階段執行。 JavaScript(簡稱JS)是前端的首要研究語言,要想真正理解JavaScript就繞不開他的運行機制--Event Loop(事件環) JS是一門...
閱讀 2305·2021-09-30 09:47
閱讀 2225·2021-09-26 09:55
閱讀 2954·2021-09-24 10:27
閱讀 1545·2019-08-27 10:54
閱讀 972·2019-08-26 13:40
閱讀 2500·2019-08-26 13:24
閱讀 2427·2019-08-26 13:22
閱讀 1736·2019-08-23 18:38