【11】JavaScript 線程機制與事件機制

JavaScript線程機制與事件機制

1、進程與線程

進程(process

  • 程序的一次執行,它佔有一片獨有的內存空間。
  • 能夠經過windows任務管理器查看進程。

線程(thread

  • 是進程內的一個獨立執行單元。
  • 是程序執行的一個完整流程。
  • CPU的最小調度單元。

進程與線程圖解

clipboard.png

相關知識

  • 應用程序必須運行在某個進程的某個線程上。
  • 一個進程中至少有一個運行的線程:主線程,進程啓動後自動建立。
  • 一個進程中也能夠同時運行多個線程,咱們會說程序是多線程運行的。
  • 一個進程內的數據能夠供其中的多個線程中直接共享。
  • 多個進程之間的數據是不能直接共享的。
  • 線程池(thread pool):保存多個線程對象的容器,實現線程對象的反覆利用。

相關問題

(1)何爲多進程與多線程?javascript

  • 多進程運行:一個應用程序能夠同時啓動多個實例運行。
  • 多線程:在一個進程內,同時有多個線程運行。

(2)比較單線程與多線程?css

  • 多線程html

    • 優勢:能有效提高CPU的利用率。
    • 缺點:java

      • 建立多線程開銷。
      • 線程間切換開銷。
      • 死鎖與狀態同步問題。
  • 單線程web

    • 優勢:順序編程簡單易懂。
    • 缺點:效率低。

(3)JS是單線程仍是多線程?ajax

  • JS是單線程運行的。
  • 可是使用H5中的 Web Workers能夠多線程運行。

(4)瀏覽器運行是單線程仍是多線程?編程

  • 瀏覽器都是多線程運行的。

(5)瀏覽器運行是單進程仍是多進程?windows

  • 有的是單進程:跨域

    • 老版Firefox
    • 老版IE
  • 有的是多進程:瀏覽器

    • Chrome
    • 新版Firefox
    • 新版IE
  • 如何查看瀏覽器是不是多進程運行的呢?

    • 任務管理器==>進程

2、瀏覽器內核

(1)瀏覽器內核是支撐瀏覽器運行的最核心的程序。

(2)不一樣的瀏覽器內核不同:

  • ChromeSafariwebkit
  • FirefoxGecko
  • IETrident
  • 360,搜狗等國內瀏覽器:Trident+webkit

(3)內核由不少模塊組成:

  • 主線程

    • js引擎模塊:負責js程序的編譯與運行。
    • html,css文檔解析模塊:負責頁面文本的解析。
    • DOM/CSS模塊:負責DOM/CSS在內存中的相關處理。
    • 佈局和渲染模塊:負責頁面的佈局和效果的繪製(內存中的對象)
  • 分線程

    • 定時器模塊:負責定時器的管理。
    • DOM事件響應模塊:負責事件的管理。
    • 網絡請求模塊:負責服務器請求(常規/ajax)。

3、定時器引起的思考

(1)定時器真是定時執行的嗎?

  • 定時器並不能保證真正定時執行。
  • 通常會延遲一丁點(能夠接受), 也有可能延遲很長時間(不能接受)。
<button id="btn">啓動定時器</button>

document.getElementById('btn').onclick = function () {
  var start = Date.now()
  console.log('啓動定時器前...')
  setTimeout(function () {
    console.log('定時器執行了', Date.now()-start)
  }, 200)
  console.log('啓動定時器後...')
}

clipboard.png

給上面回調函數加一個長時間的任務:

document.getElementById('btn').onclick = function () {
  var start = Date.now()
  console.log('啓動定時器前...')
  setTimeout(function () {
    console.log('定時器執行了', Date.now()-start)
  }, 200)
  console.log('啓動定時器後...')
  // 作一個長時間的工做
  for (var i = 0; i < 1000000000; i++) {}
}

結果:
clipboard.png

同步任務執行完以後纔會執行異步任務。

(2)定時器回調函數是在分線程執行的嗎?

  • 在主線程執行的, js是單線程的。

(3)定時器是如何實現的?

  • 事件循環模型(後面講)。

4、JS是單線程執行的

(1)如何證實js執行是單線程的?

  • setTimeout()的回調函數是在主線程執行的。
  • 定時器回調函數只有在運行棧中的代碼所有執行完後纔有可能執行。

(2)爲何js要用單線程模式, 而不用多線程模式?

  • JavaScript的單線程,與它的用途有關。
  • 做爲瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操做DOM
  • 這決定了它只能是單線程,不然會帶來很複雜的同步問題

(3)代碼的分類:

  • 初始化代碼
  • 回調代碼

(4)js引擎執行代碼的基本流程

  • 先執行初始化代碼: 包含一些特別的代碼 ,回調函數(異步執行)

    • 設置定時器
    • 綁定監聽
    • 發送ajax請求
  • 後面在某個時刻纔會執行回調代碼。

5、瀏覽器的事件循環(輪詢)模型

瀏覽器的事件循環模型原理圖

clipboard.png

相關重要概念

(1)執行棧

  • execution stack
  • 全部的代碼都是在此空間中執行的。

(2)瀏覽器內核

  • browser core
  • js引擎模塊(在主線程處理)
  • 其它模塊(在主/分線程處理)

(3)任務隊列(callback queue)

task queue

(4)消息隊列(callback queue)

message queue

(5)事件隊列(callback queue)

event queue

(6)事件輪詢

  • event loop
  • 從任務隊列中循環取出回調函數放入執行棧中處理(一個接一個)。

(7)事件驅動模型

event-driven interaction model

(8)請求響應模型

request-response model

執行流程

(1)全部代碼分類:

  • 初始化執行代碼:包含綁定dom事件監聽, 設置定時器, 發送ajax請求的代碼。
  • 回調執行代碼:處理回調邏輯。

(2)js引擎執行代碼的基本流程:

  • 初始化代碼===>回調代碼

(3)模型的2個重要組成部分:

  • 事件管理模塊
  • 回調隊列

(4)模型的運轉流程

​ (a)執行初始化代碼, 將事件回調函數交給對應模塊管理。

​ (b)當事件發生時, 管理模塊會將回調函數及其數據添加到回調列隊中。

​ (c)只有當初始化代碼執行完後(可能要必定時間), 纔會遍歷讀取回調隊列中的回調函數執行。

6、H5 Web Workers(多線程)

介紹

  • Web WorkersHTML5 提供的一個Javascript多線程解決方案。
  • 咱們能夠將一些大計算量的代碼交由Web Worker運行而不凍結用戶界面。
  • 可是子線程徹底受主線程控制,且不得操做DOM。因此,這個新標準並無改變JavaScript單線程的本質

使用

  • 相關API

    • Worker: 構造函數, 加載分線程執行的js文件。
    • Worker.prototype.onmessage: 用於接收另外一個線程的回調函數。
    • Worker.prototype.postMessage: 向另外一個線程發送消息。
  • 建立在分線程執行的JS文件
var onmessage = function (event){ //不能用函數聲明
    console.log('onMessage()22');
    var upper = event.data.toUpperCase();//經過event.data得到發送來的數據
    postMessage( upper );//將獲取到的數據發送回主線程
}
  • 在主線程中的JS中發消息並設置回調
//建立一個Worker對象並向它傳遞將在新線程中執行的腳本的URL
var worker = new Worker("worker.js");  
//接收worker傳過來的數據函數
worker.onmessage = function (event) {     
    console.log(event.data);             
};
//向worker發送數據
worker.postMessage("hello world");

圖解

clipboard.png

應用練習

編程實現斐波那契數列(Fibonacci sequence)的計算

F(0)=0,F(1)=1,..... F(n)=F(n-1)+F(n-2)
  • 直接在主線程:
var fibonacci =function(n) {
    return n <2 ? n : fibonacci(n -1) + fibonacci(n -2);
};
console.log(fibonacci(48));
  • 使用Web Workers在分線程:

    • 主線程:
    var worker = new Worker('worker2.js');
    worker.addEventListener('message', function (event) {
        var timer2 = new Date().getTime();
        console.log('結果:' + event.data, '時間:' + timer2, '用時:' + ( timer2 - timer ));
    }, false);
    var timer = new Date().getTime();
    console.log('開始計算: ', '時間:' + timer);
    setTimeout(function () {
        console.log('定時器函數在計算數列時執行了', '時間:' + new Date().getTime());
    }, 1000);
    worker.postMessage(40);
    console.log('我在計算數列的時候執行了', '時間:' + new Date().getTime());
    • 分線程:
    var fibonacci =function(n) {
        return n <2 ? n : fibonacci(n -1) + fibonacci(n -2);
    };
    var onmessage = function(event) {
        var n = parseInt(event.data, 10);
        postMessage(fibonacci(n));
    };

不足

  • 慢。
  • 不能跨域加載JS
  • worker內代碼不能訪問DOM(更新UI)。
  • 不是每一個瀏覽器都支持這個新特性。
相關文章
相關標籤/搜索