1. 程式人生 > >JS的瀏覽器環境的事件迴圈(event loop)機制

JS的瀏覽器環境的事件迴圈(event loop)機制

1. 事件迴圈

JavaScript引擎並不是獨立執行的,它執行在宿主環境中,對多數開發者來說通常就是Web瀏覽器。經過最近幾年的發展,JavaScript已經超出了瀏覽器的範圍,進入了其他環境,比如通過像Node.js這樣的工具進入伺服器領域。實際上,JavaScript現如今已經嵌入到了從機器人到電燈泡等各種各樣的裝置中。
所有這些環境都有一個共同“點”,即它們都提供了一種機制來處理程式中多個塊的執行,且執行每塊時呼叫JavaScript引擎,這種機制被稱為事件迴圈。
換句話說,JavaScript引擎本身並沒有時間的概念,只是一個按需執行JavaScript任意程式碼片段的環境。“事件”(JavaScript程式碼執行)排程總是由包含它的環境進行。
引自《你不知道的JavaScript中卷》第二部分 非同步和效能 第一章 非同步

2. JS引擎的兩大特點:單執行緒和非阻塞

單執行緒

JS引擎是基於單執行緒(Single-threaded)事件迴圈的概念構建的。同一時刻只執行一個程式碼塊在執行,與之相反的是像JAVA和C++一樣的語言,它們允許多個不同的程式碼塊同時執行。對於基於執行緒的軟體而言,當多個程式碼塊同時訪問並改變狀態時,程式很難維護並保證狀態不出錯。

非阻塞

非阻塞則是當代碼需要進行一項非同步任務(無法立刻返回結果,需要花一定時間才能返回的任務,如I/O事件)的時候,主執行緒會掛起(pending)這個任務,然後在非同步任務返回結果的時候再根據一定規則去執行相應的回撥。非阻塞是通過事件迴圈機制實現的。
JS通常是非阻塞的,除了某些特殊情況,JS會停止程式碼執行:
- alert, confirm, prompt(除了Opera)
- “頁面上的程式正忙”的系統對話方塊彈出

2.同步和非同步

非同步程式設計模型
包括事件模型,回撥模式,Promise非同步操作結果佔位符。
事件模型會在觸發事件後向任務佇列中新增事件處理程式,但是隻有當其前面的任務都完成後它才會執行。
回撥模式和事件模型類似,非同步程式碼都會在未來某個事件點執行,但是不同在於回撥模型中被呼叫的回撥函式是作為引數傳入的,而且可以使用回撥模式連結多個呼叫,也就是也巢狀的方式呼叫回撥函式,但是這種方式的問題是會陷入“回撥地獄”,巢狀的程式碼難以理解並難以維護。
Promise不會訂閱一個事件或是傳遞迴調函式給目標函式,而是讓函式返回一個Promise物件。Promise有三種狀態,儲存在其內部屬性中,分別是”pending”(進行中)、fulfilled(已完成)、rejected(已拒絕)。通過為執行器函式分別指定resolve和reject函式可以在執行器完成時(fulfilled或rejected)呼叫相應的函式。

3.執行棧與任務佇列

棧記憶體和堆記憶體
棧記憶體儲存著JS的變數和指向堆記憶體中物件的指標,堆記憶體儲存著物件。
下面的執行棧和棧記憶體是兩個概念。
執行棧和任務佇列
這裡寫圖片描述

呼叫棧中遇到DOM操作、ajax請求以及setTimeout等WebAPIs的時候就會交給瀏覽器核心的其他模組進行處理,webkit核心在Javasctipt執行引擎之外,有一個重要的模組是webcore模組。對於圖中WebAPIs提到的三種API,webcore分別提供了DOM Binding、network、timer模組來處理底層實現。等到這些模組處理完這些操作的時候將回調函式放入任務佇列中,之後等棧中的task執行完之後再去執行任務佇列之中的回撥函式。
函式呼叫形成了一個棧幀

function foo(b) {
  var a = 10;
  return a + b + 11;
}

function bar(x) {
  var y = 3;
  return foo(x * y);
}

console.log(bar(7));

呼叫bar時,建立了第一個幀 ,幀中包含了bar的引數和區域性變數。當bar呼叫foo時,第二個幀就被建立,並被壓到第一個幀之上,幀中包含了foo的引數和區域性變數。當foo返回時,最上層的幀就被彈出棧(剩下bar函式的呼叫幀 )。當bar返回的時候,棧就是空的。以上都是同步程式碼的執行。

任務佇列
一個 JavaScript 執行時包含了一個待處理的訊息佇列。每一個訊息都與一個函式相關聯。當棧擁有足夠記憶體時,從佇列中取出一個訊息進行處理。這個處理過程包含了呼叫與這個訊息相關聯的函式(以及因而建立了一個初始堆疊幀)。當棧再次為空的時候,也就意味著訊息處理結束。

4.任務佇列中的巨集觀任務和微觀任務

任務佇列並不是只有一個,不同的任務對應著不同的任務佇列。巨集觀任務放入巨集觀任務佇列,微觀任務放入微觀任務佇列,這些任務佇列在棧空的時候被調入的優先順序是微觀任務佇列優於巨集觀任務佇列,當微觀任務佇列都清空的時候才執行巨集觀任務佇列中的任務

macro-task包括:script(整體程式碼), setTimeout, setInterval, setImmediate, I/O, UI rendering。
micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver,MessageChannel

5.例項

理解下列程式碼的輸出順序:
5.1SetTimeOut

for (var i = 0; i < 5; i++) {
    setTimeout(function() {
      console.log(i);
    }, 1000);
}
console.log(i);

結果:
輸出的狀態就是:5 -> 5,5,5,5,5,即第1個 5 直接輸出,最少等待1s之後,輸出 5個5;
因為執行for迴圈的時候,執行引擎將setTimeout加入到執行棧中,呼叫棧發現setTimeout是之前提到的WebAPIs中的API,因此將其出棧之後將延時執行的匿名函式交給瀏覽器的timer模組進行處理。當timer模組中延時方法規定的時間到了之後就將其放入到任務佇列之中。什麼時候執行呢?等到執行棧空的時候執行回撥函式。for迴圈之後執行了console語句,直接輸出5,此時執行棧空,開始執行任務佇列中的5個回撥函式。因為這些回撥函式都是在1秒之後依次加入到任務佇列的,因此1秒之後,連續輸出了5個5。
5.2 Promise

(function test() {
    setTimeout(function() {console.log(4)}, 0);
    new Promise(function executor(resolve) {
        console.log(1);
        for( var i=0 ; i<10000 ; i++ ) {
            i == 9999 && resolve();
        }
        console.log(2);
    }).then(function() {
        console.log(5);
    });
    console.log(3);
})();

結果:
1,2,3,5,4
2,3,5resolve函式會加入微觀任務佇列,因此會先執行2再執行3,此時執行棧清空,開始清理微觀任務佇列中的函式,因此輸出5
5,4先執行微觀任務佇列中的resolve函式再執行巨集觀任務中的SetTimeout函式,因此先輸出5再輸出4

6.參考

相關推薦

JS瀏覽器環境事件迴圈(event loop)機制

1. 事件迴圈 JavaScript引擎並不是獨立執行的,它執行在宿主環境中,對多數開發者來說通常就是Web瀏覽器。經過最近幾年的發展,JavaScript已經超出了瀏覽器的範圍,進入了其他環境,比如通過像Node.js這樣的工具進入伺服器領域。實際上,

Javascript事件迴圈——Event loop

引言 Javascript是一門單執行緒的指令碼語言,無法進行多執行緒程式設計; 因此為了不阻塞程式設計,Javascript通過事件迴圈的方式解決耗時任務,實現類多執行緒程式設計; 單執行緒 單執行緒意味著在瀏覽器中,同一時間只能做一件事,其他的行為和事件都

JavaScipt 中的事件迴圈(event loop),以及微任務 和巨集任務的概念

說事件迴圈(event loop)之前先要搞清楚幾個問題。 1. js為什麼是單執行緒的?   試想一下,如果js不是單執行緒的,同時有兩個方法作用dom,一個刪除,一個修改,那麼這時候瀏覽器該聽誰的?這就是js被設計成單執行緒的原因。   2.js為什麼需要非同步?

一文梳理JavaScript 事件迴圈(Event Loop)

事件迴圈(Event Loop),是每個JS開發者都會接觸到的概念,但是剛接觸時可能會存在各種疑惑。 眾所周知,JS是單執行緒的,即同一時間只能執行一個任務。一般情況下這不會引發問題,但是如果我們有一個耗時較多的任務,我們必須等該任務執行完畢才能進入下一個任務,然而等待的這段時間常常讓我們無法忍受,因為我們

JS Event Loop 機制看 Vue 中 nextTick 的實現原理

本文來自作者 大師兄 在 GitChat 上分享「從 JS Event Loop 機制看 Vue 中 nextTick 的實現原理」,「閱讀原文」檢視交流實錄 「文末高能」 編輯 | 泰龍 作為一名前端,一直以來以精通 Javascript 為目標。其實說實話精通真的挺難,不是你記住全部的 AP

setTimeout 是到了xx ms 就執行嗎,瞭解瀏覽器Event-Loop 機制

> 要想 JavaScript 玩得溜,還得了解波 JavaScript 執行機制/(ㄒoㄒ)/~~。 > 個人部落格:[https://shansan.top](https://shansan.top) ## 前言 最近看了波 JavaScript 相關的文章,不得不說,JavaScript 我還真沒

JS——瀏覽器關閉事件

win listener lis pushstate document fun ops turn class onbeforeunload 關閉或刷新瀏覽器會觸發此事件 window.onbeforeunload = function(ev) {

[JS]你不知道的Event Loop

從一道面試題說起 setTimeout(function() { console.log(111); }, 0); // 這裡定時器時間設定為0ms後執行 console.log(222); 相信這道題很多人都看過,結果是先輸出222,再輸出111 可能新手會犯錯,認為定時器設

JS專題之事件迴圈

準備知識 1. 程序(process) 程序是系統資源分配一個獨立單位,一個程式至少有一個程序。比方說:一個工廠代表一個 CPU, 一個車間就是一個程序,任一時刻,只能有一個程序在執行,其他程序處於非執行狀態。 2. 執行緒(Thread) 執行緒是CPU排程和分派的基本單位,一個執行緒只能屬於一個程

js 瀏覽器滾動事件

<html> <head> <script type="text/javascript" src="jquery.js"></script> <style type="text/cs

【nodejs原理&原始碼賞析(7)】【譯】Node.js中的事件迴圈,定時器和process.nextTick

目錄 Event Loop 是什麼? Event Loop 基本解釋 事件迴圈階段概覽 事件迴圈細節 timers pending callbacks poll

破陣九解:Node和瀏覽器事件迴圈/任務佇列/非同步順序/資料結構

前言 本文內容比較長,請見諒。如有評議,還請評論區指點,謝謝大家! >> 目錄 開門見山:Node和瀏覽器的非同步執行順序問題 兩種環境下的巨集任務和微任務(macrotask && microtask) Node和瀏覽器的事件迴圈

setTimeOut引發的思考——初步理解JS事件迴圈機制 Event Loop

JS是單執行緒引擎,線上程中擁有唯一一個事件迴圈(web workder涉及到了多執行緒,再做補充) JS程式碼執行過程中,除了依靠函式呼叫棧順序執行JS程式碼,還依靠任務佇列(task queue)執行一些程式碼。 一個執行緒中,事件迴圈是唯一的,但是任務佇列

JS事件迴圈機制event loop

一 前言 相信所有學過 JavaScript 都知道它是一門單執行緒的語言,這也就意味著 JS 無法進行多執行緒程式設計,但是 JS 當中卻有著無處不在的非同步概念 。在初期許多人會把非同步理解成類似多執行緒的程式設計模式,其實他們中有著很大的差別,要完全理解非

node 中談談 Event Loop事件迴圈機制

在講 Event Loop (事件迴圈)之前,我們來了解點 node 的東西,來幫助我們更加明白事件迴圈是幹什麼的 Node 是什麼 Node.js 是一個基於 Chrome V8 引擎的 JavaScript 執行環境,Node 不是一門語言,是讓 js 執行在後端的,執行時不包括 js

event loop到async await來了解事件迴圈機制

JS為什麼是單執行緒的? 最初設計JS是用來在瀏覽器驗證表單操控DOM元素的是一門指令碼語言,如果js是多執行緒的那麼兩個執行緒同時對一個DOM元素進行了相互衝突的操作,那麼瀏覽器的解析器是無法執行的。 JS為什麼需要非同步? 如果JS中不存在非同步,只能自上而下執行,如果上一行解析時間很長,那麼下面的

node.js中對Event Loop事件迴圈的理解

javascript是單執行緒的,所以任務的執行都需要排隊,任務分為兩種,一種是同步任務,一種是非同步任務。 同步任務是進入主執行緒上排隊執行的任務,上一個任務執行完了,下一個任務才會執行。 非同步任務是不進入主執行緒,而是進入一個 "任務佇列" 裡,"任務佇列" 通知主執行緒,該非同步任務才會進入主執行

詳解JavaScript中的Event Loop事件迴圈機制

前言我們都知道,javascript從誕生之日起就是一門單執行緒的非阻塞的指令碼語言。這是由其最初的用途來決定的:與瀏覽器互動。單執行緒意味著,javascript程式碼在執行的任何時候,都只有一個主執行緒來處理所有的任務。而非阻塞則是當代碼需要進行一項非同步任務(無法立刻返

event loop js事件迴圈 microtask macrotask

放個面試題,拋個磚: console.log('start') const interval = setInterval(() => { console.log('setInterval') }, 0) setTimeout(() =>

JS瀏覽器賭博網站平臺出租事件迴圈機制

程序、執行緒程序賭博網站平臺出租【大神原始碼論壇】dsluntan.com  【布丁原始碼論壇】budingbbs.com 企娥3393756370是系統分配的獨立資源,是 CPU 資源分配的基本單位,程序是由一個或者多個執行緒組成的。執行緒是程序的執行流,是CPU排程和分派