JS基礎-事件隊列


為什么JavaScript是單線程?

JavaScript語言的一大特點就是單線程,也就是說,同一個時間只能做一件事。那么,為什么JavaScript不能有多個線程呢?這樣能提高效率啊。

JavaScript的單線程,與它的用途有關。作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操作DOM。這決定了它只能是單線程,否則會帶來很復雜的同步問題。比如,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上添加內容,另一個線程刪除了這個節點,這時瀏覽器應該以哪個線程為准?

所以,為了避免復雜性,從一誕生,JavaScript就是單線程,這已經成了這門語言的核心特征,將來也不會改變。

為了利用多核CPU的計算能力,HTML5提出Web Worker標准,允許JavaScript腳本創建多個線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個新標准並沒有改變JavaScript單線程的本質。

Event Loop

參考地址:Event Loop 這個循環你曉得么?(附 GIF 詳解)-餓了么前端

任務隊列的本質

  • 所有同步任務都在主線程上執行,形成一個執行棧(execution context stack)。
  • 主線程之外,還存在一個”任務隊列”(task queue)。只要異步任務有了運行結果,就在”任務隊列”之中放置一個事件。
  • 一旦”執行棧”中的所有同步任務執行完畢,系統就會讀取”任務隊列”,看看里面有哪些事件。那些對應的異步任務,於是結束等待狀態,進入執行棧,開始執行。
  • 主線程不斷重復上面的第三步。

異步任務

  • setTimeOut、setInterval
  • DOM 事件
  • Promise

JavaScript 實現異步編程的方法?

  • 回調函數
  • 事件監聽
  • 發布/訂閱
  • Promises 對象
  • Async 函數[ES7]

關於 setTimeOut、setImmediate、process.nextTick()的比較

setTimeout()

將事件插入到了事件隊列,必須等到當前代碼(執行棧)執行完,主線程才會去執行它指定的回調函數。
當主線程時間執行過長,無法保證回調會在事件指定的時間執行。
瀏覽器端每次setTimeout會有4ms的延遲,當連續執行多個setTimeout,有可能會阻塞進程,造成性能問題。

setImmediate()

事件插入到事件隊列尾部,主線程和事件隊列的函數執行完成之后立即執行。和setTimeout(fn,0)的效果差不多。
服務端node提供的方法。瀏覽器端最新的api也有類似實現:window.setImmediate,但支持的瀏覽器很少。

process.nextTick()

插入到事件隊列尾部,但在下次事件隊列之前會執行。也就是說,它指定的任務總是發生在所有異步任務之前,當前主線程的末尾。
大致流程:當前”執行棧”的尾部–>下一次Event Loop(主線程讀取”任務隊列”)之前–>觸發process指定的回調函數。
服務器端node提供的辦法。用此方法可以用於處於異步延遲的問題。
可以理解為:此次不行,預約下次優先執行。

瀏覽器的Tasks、microtasks、 queues 和 schedules

Promise

Promise本身是同步的立即執行函數, 當在 executor 中執行 resolve 或者 reject 的時候, 此時是異步操作, 會先執行 then/catch 等,當主棧完成后,才會去調用 resolve/reject 中存放的方法執行,打印 p 的時候,是打印的返回結果,一個 Promise 實例。

async await

Async/Await就是一個自執行的generate函數。利用generate函數的特性把異步的代碼寫成“同步”的形式。

async 函數返回一個 Promise 對象,當函數執行的時候,一旦遇到 await 就會先返回,等到觸發的異步操作完成,再執行函數體內后面的語句。可以理解為,是讓出了線程,跳出了 async 函數體。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM