1. 程式人生 > 程式設計 >node.js中對Event Loop事件迴圈的理解與應用例項分析

node.js中對Event Loop事件迴圈的理解與應用例項分析

本文例項講述了node.js中對Event Loop事件迴圈的理解與應用。分享給大家供大家參考,具體如下:

javascript是單執行緒的,所以任務的執行都需要排隊,任務分為兩種,一種是同步任務,一種是非同步任務。

同步任務是進入主執行緒上排隊執行的任務,上一個任務執行完了,下一個任務才會執行。

非同步任務是不進入主執行緒,而是進入一個 "任務佇列" 裡,"任務佇列" 通知主執行緒,該非同步任務才會進入主執行緒執行。

任務的執行機制如下:

1、所有同步任務在主執行緒上執行,形成一個 "執行棧",注意棧是先進後出的。

2、主執行緒外,有一個 "任務佇列" ,只要非同步任務處理完有結果了,就在 "任務佇列" 中放置一個事件,注意佇列是先進先出的。

3、一旦 "執行棧" 中所有同步任務執行完畢。系統讀取 "任務佇列" 中的事件,對應的非同步任務。放入 "執行棧" 中,開始執行。

4、主執行緒不斷重複第三步,這種迴圈從 "任務佇列" 中讀取事件處理的這種執行機制稱為Event Loop(事件迴圈)。

"執行棧" 中的同步程式碼總是比 "任務佇列"中的非同步任務之前執行。

function fun() {
  setTimeout(function () {
    console.log('非同步任務');
  },0);
  console.log(1);
  console.log(2);
  console.log(3);
  console.log(4);
  console.log(5);
}
fun();

上面的程式碼,console.log程式碼寫在setTimeout後面,但仍然先執行。

"任務佇列" 是一個佇列,佇列的特性是先進先出。看下面程式碼:

function fun() {
  console.log(1);
  setTimeout(function () {
    console.log(2);
    setTimeout(function () {
      console.log(3);
    },0);
  },0);
  console.log(4);
}
fun();

輸出結果為 1 4 2 3,列印 2 的setTimeout任務比列印 3 的setTimeout任務先進入佇列,所以會先執行。

對於非同步操作,像ajax,只有操作成功後返回結果,才會進入 "任務佇列" 中,而不是呼叫的時候就放入佇列中。看下面程式碼:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<script>
  function ajax() {
    var xhr = new XMLHttpRequest();
    xhr.open('GET','https://mail.163.com/',true);
    xhr.send();
    xhr.onreadystatechange = function () {
      if (xhr.readyState == 4 && xhr.status == 200) {
        console.log(xhr.responseText);
      }
    };
  }
  function fun() {
    console.log(1);
    ajax();
    setTimeout(function () {
      console.log(2);
    },1000);
    console.log(3);
  }
  fun();
</script>
</body>
</html>

ajax() 與 setTimeout 誰先進入佇列,誰先輸出,是需要看兩者消耗時間,誰更短。時間短的會先進入佇列先執行。

setTimeout 與 setInterval 執行機制一樣,都是在指定時間把事件插入到 "任務佇列" 尾部。區別是前者只執行一次,後者可反覆執行。

node.js 還為我們提供了,process.nextTick 和 setImmediate 與 "任務佇列" 有關的方法。

process.nextTick 會把回撥函式放在當前 "執行棧" 的尾部。也就是說是在讀取 "任務佇列" 之前執行。

function fun() {
  console.log(1);
  setTimeout(function () {
    console.log(2);
  },0);
  process.nextTick(function () {
    console.log(3);
    process.nextTick(function () {
      console.log(4);
    });
  });
  process.nextTick(function () {
    console.log(5);
  });
  console.log(6);
}
fun();

上面的程式碼會輸出 1 6 3 5 4 2 ,注意process.nextTick會把回撥函式放在 "執行棧" 的尾部。

同步程式碼最先輸出 1 6,然後 3 的先放入尾部,然後 5 的跟在 3 後面。3先執行,然後把 4 放入到 5 的後面。5執行完後,再執行4,最後讀取 "任務佇列" 中的輸出2。

setImmediate 會把回撥函式放在當前 "任務佇列" 的尾部。也就是下一次事件迴圈Event Loop時執行。

function fun() {
  console.log(1);
  setTimeout(function () {
    console.log(2);
  },0);
  setImmediate(function () {
    console.log(3);
  });
  console.log(4);
}
fun();

上面的程式碼是會輸出 1 4 2 3 還是 1 4 3 2 是不確定的,因為setTimeout 與 setImmediate 都會在下一次事件迴圈Event Loop中觸發,所以輸出是不確定的。

希望本文所述對大家node.js程式設計有所幫助。