js 事件循環中的job queue和message queue

本文討論的事件循環均是基於瀏覽器環境上的,相似nodejs環境下的事件循環與此並不相同。javascript

讀者首先要對js單線程事件循環機制以及Promise有基本理解;若是這兩個概念不是很清楚,建議先閱讀下面兩篇文章:java

THE JAVASCRIPT EVENT LOOP ; Promise 對象node

本文是基於THE JAVASCRIPT EVENT LOOP ,並對其內容的延伸,因此下面提到的概念都按這篇文章的來。首先我會總結一下 THE JAVASCRIPT EVENT LOOP 。OK,讓咱們開始吧。es6

1,消息隊列(message queue)

      咱們知道js單線程的實現方式會把異步任務(setTimeout回調函數,事件監聽回調函數等)放在一個消息隊列中;當主任務隊列任務爲空時會去message queue查詢是否有等待執行的任務,若是有則執行。chrome

 例1:promise

var task_in_message_queue = () => {console.log('task in message queue')}
setTimeout(task_in_message_queue,0);
console.log('main task');

//result:
//main task
//task in message queue

setTimeout函數將task_in_message_queue函數添加到message queue隊列中。等到主任務隊列執行完成時(此時已打印main task),執行存在message queue隊列中的task_in_message_queue函數瀏覽器

2,任務隊列(job queue)

        ES6中引入了任務隊列來執行Promise的回調函數。同message queue同樣,job queue中的任務也是在主任務隊列爲空時纔開始執行。異步

例2:函數

var promise = new Promise((resolve,reject) => {
    resolve('task in job queue');
});
var resolve_callback = (resolve_message) => {console.log(resolve_message)}
promise.then(resolve_callback);
console.log('main task');

//result:
//main task
//task in job queue

/**
這裏有一個有趣的現象

在chrome中打印出的結果是
main task
task in job queue
undefined  //主任務的函數返回值

在firefox中的結果是
main task 
undefined  //主任務的函數返回值
task in job queue

感受v8的實現是把job queue整合到了主任務隊列尾部
**/

promise.then 將promise fulfilled狀態下的回調函數resolve_callback添加到job queue中。等到主任務隊列執行完成時(此時已打印main task),執行存在job queue隊列中的resolve_callback函數oop

這裏有一點須要注意的是promise構造函數會在主任務中當即執行,例子以下:

var promise = new Promise((resolve,reject) => {
    resolve('task in job queue');
    console.log('the promise construction executed');
});
var resolve_callback = (resolve_message) => {console.log(resolve_message)}
promise.then(resolve_callback);
console.log('main task');


//result:
//the promise construction executed
//main task
//task in job queue

3,任務隊列(job queue)VS 消息隊列(message queue)

       經過上面的例子咱們知道主任務隊列優先級是最高的,那麼job queue和message queue哪一個優先級更高呢?答案是job queue,js會將job queue中的任務徹底執行完以後再執行message queue中的任務。例子以下:

var message_task = () => {console.log('message task');}
setTimeout(message_task,0);
var promise1 = new Promise((resolve,reject) => {
    resolve('promise 1 resolved');
});
var promise2 = new Promise((resolve,reject) => {
    resolve('promise 2 resolved');
});
var resolve_callback = (resolve_message) => {console.log(resolve_message)}
promise1.then(resolve_callback);
promise2.then(resolve_callback);
console.log('main task');


//result:
//main task
//promise 1 resolved
//promise 2 resolved
//message task

/**
這裏chrome和firefox返回undefined的位置同上面的例子同樣,也是不一樣的。有興趣的話能夠試試看一下。
**/

4,每次執行message queue中的任務前都會檢查job queue嗎?

        如今咱們知道job queue的優先級高於message queue。那麼每次執行message queue中任務前會檢查job queue嗎?個人意思是若是當前job queue爲空,message queue中有多個任務(假設有m_task1和m_task2)。js開始執行message queue中的任務,在執行完m_task1時插入了一個j_task1在job queue中。那麼接下來是先執行m_task2呢仍是j_task1呢?若是先執行了m_task2的話,就說明js一旦開始執行message queue中的任務就會將全部message queue中任務執行完再檢查其它任務隊列。若是先執行j_task1的話,那麼說明再執行每一個message queue中的任務前都會先檢查其它任務隊列,先執行優先級高的任務隊列中的任務。爲此咱們用以下代碼來檢驗:

var promise_task = new Promise((resolve,reject) => {
    resolve('j_task1');
});
var resolve_callback = (resolve_message) => {console.log(resolve_message)}
var message_task1 = () => {
    promise_task.then(resolve_callback);
    console.log('m_task1');
}
var message_task2 = () => {console.log('m_task2');}
setTimeout(message_task1,0);
setTimeout(message_task2,0);


//result:
//m_task1
//j_task1
//m_task2

事實證實js在每次執行message queue中的任務前都會檢查其它任務隊列(至少會檢查job queue),根據隊列優先級決定先執行哪一個隊列中的任務。

5,主任務隊列呢?

        上面咱們瞭解了job queue和message queue中任務的執行順序,簡而言之:在每次一個任務結束時,js都會根據任務隊列的優先級判斷下一個執行任務是哪一個。若是job queue中有任務則執行job queue中的第一個任務,不然執行message queue中的第一個任務。那麼主任務隊列是否是也同樣呢?(邏輯上應該是同樣的,不然job queue或者message queue中的任務能夠遞歸建立新任務,這樣就永遠沒法回到主任務隊列了)。

        即每次選擇執行任務前(或者每次任務結束後),js會根據主任務隊列,job queue,message queue的優先級來挑選將要執行下一個任務是哪一個。

        爲此咱們聲明一個promise和一個message_task函數。在這個promise的回調函數中使用setTimeout建立一個message_task的message queue任務,同時在message_task中調用promise.then 函數建立一個job queue 任務。這樣兩個任務會循環建立並循環執行。運行後咱們會在console中看到兩個任務循環打印,這是咱們在console中鍵入alert('stop')命令。若是頁面顯示了alert,console中止了打印就說明主任務隊列的行爲方式和job queue,message queue是同樣的。不然的話,在這種狀況下咱們將永遠沒法回到主任務隊列。驗證代碼以下:

var promise_task = new Promise((resolve,reject) => {
    resolve('j_task');
});
var resolve_callback = (resolve_message) => {
    setTimeout(message_task,0);
    console.log(resolve_message);
}
var message_task = () => {
    promise_task.then(resolve_callback);
    console.log('m_task');
}

promise_task.then(resolve_callback);


//result:
//console會循環打印 j_task 和 m_task
//這是在console中鍵入alert('stop')命令,觀察是否彈出alert框,console中打印是否終止

但願你們自行求證一下,固然驗證完畢後記得刷新頁面,否則可能就崩了。另:最好在chrome下驗證,firefox有些卡頓。

總結

        js事件循環規律可大體總結爲以下:

        1,js中有三個任務隊列:主任務隊列,job queue,message queue;

        2,它們的優先級是:主任務隊列 > job queue > message queue;

        3,每當要執行下一個任務前(或者一個任務完成後),js會根據優先級詢問各個任務隊列是否爲空,一旦遇到非空任務隊列時則取其第一個任務執行。

相關文章
相關標籤/搜索