重學前端學習筆記(十七)--Promise裏的代碼爲何比setTimeout先執行?

筆記說明

重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,天天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些要點筆記以及感悟,完整的能夠加入winter的專欄學習【原文有winter的語音】,若有侵權請聯繫我,郵箱:kaimo313@foxmail.com。前端

1、引言

一個 JavaScript 引擎會常駐於內存中,它等待着 宿主JavaScript 代碼或者函數傳遞給它執行。瀏覽器

一、ES3版本以及以前,JavaScript 自己尚未異步執行代碼的能力,宿主環境傳遞給 JavaScript 引擎,而後按順序執行,由宿主發起任務。異步

二、ES5以後,JavaScript 引入了 Promise,不須要瀏覽器的安排,JavaScript 引擎自己也能夠發起任務。async

三、採納JSC引擎術語,把宿主發起的任務稱爲宏觀任務,把JavaScript引擎發起的任務稱爲微觀任務函數

2、宏觀和微觀任務

JavaScript 引擎等待宿主環境分配宏觀任務,在 Node 術語中,把這個部分稱爲事件循環學習

一、用僞代碼來表示:跑在獨立線程中的循環ui

while(TRUE) {
    r = wait();
    execute(r);
}
複製代碼

二、整個循環作的事情基本上就是反覆 等待 - 執行,這裏的執行過程,其實都是一個宏觀任務。能夠大體理解爲:宏觀任務的隊列就至關於時間循環。spa

三、在宏觀任務中,JavaScriptPromise 還會產生異步代碼JavaScript 必須保證這些異步代碼在一個宏觀任務中完成,所以,每一個宏觀任務中又包含了一個微觀任務隊列:以下圖所示線程

宏觀任務中的微觀任務隊列

例如:Promise 永遠在隊列尾部添加微觀任務。setTimeout 等宿主 API,則會添加宏觀任務。code

3、Promise

JavaScript 語言提供的一種標準化的異步管理方式,當進行 io、等待或者其它異步操做的函數,不返回真實結果,而返回一個承諾,函數的調用方能夠在合適的時機,選擇等待這個承諾兌現

3.一、基本用法示例

function sleep(duration) {
        return new Promise(function(resolve, reject) {
            setTimeout(resolve,duration);
        })
    }
    sleep(1000).then( ()=> console.log("finished"));
複製代碼

Promisethen 回調是一個異步的執行過程。

3.二、Promise 函數中的執行順序

var r = new Promise(function(resolve, reject){
        console.log("a");
        resolve()
    });
    r.then(() => console.log("c"));
    console.log("b")

    // 輸出順序:a b c
複製代碼

3.三、setTimeout 混用的 Promise

var r = new Promise(function(resolve, reject){
        console.log("a");
        resolve()
    });
    setTimeout(()=>console.log("d"), 0)
    r.then(() => console.log("c"));
    console.log("b")

    // 輸出順序:a b c d
複製代碼

Promise 產生的是 JavaScript 引擎內部的微任務,而 setTimeout 是瀏覽器 API,它產生宏任務。因此d 一定在 c 以後輸出。

3.四、一個耗時 1 秒的 Promise

setTimeout(()=>console.log("d"), 0)
    var r = new Promise(function(resolve, reject){
        resolve()
    });
    r.then(() => {
        var begin = Date.now();
        while(Date.now() - begin < 1000);
        console.log("c1")
        new Promise(function(resolve, reject){
            resolve()
        }).then(() => console.log("c2"))
    });

    // 輸出順序:c1 c2 d
複製代碼

這個例子很好的解釋了微任務優先的原理

3.五、如何分析異步執行的順序

  • 一、首先咱們分析有多少個宏任務
  • 二、在每一個宏任務中,分析有多少個微任務
  • 三、根據調用次序,肯定宏任務中的微任務執行次序
  • 四、根據宏任務的觸發規則和調用次序,肯定宏任務的執行次序
  • 五、肯定整個順序
function sleep(duration) {
        return new Promise(function(resolve, reject) {
            console.log("b");
            setTimeout(resolve,duration);
        })
    }
    console.log("a");
    sleep(5000).then(()=>console.log("c"));

    // 輸出順序:a b c(c要等5秒)
複製代碼

第一個宏觀任務中,包含了前後同步執行的 console.log("a"); 和 console.log("b");。

setTimeout 後,第二個宏觀任務執行調用了 resolve,而後 then 中的代碼異步獲得執行,調用了 console.log("c")

4、新特性:async/await

async/awaitES2016 新加入的特性,它提供了用 for、if 等代碼結構來編寫異步的方式,而且運行時基礎是 Promise

一、async 函數是在 function 關鍵字以前加上 async 關鍵字,這樣就定義了一個 async 函數,能夠在其中使用 await 來等待一個 Promise

function sleep(duration) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve,duration);
    })
}
async function foo(){
    console.log("a")
    await sleep(2000)
    console.log("b")
}

foo();

// 輸出順序:a b(b要等兩秒)
複製代碼

二、async 嵌套

function sleep(duration) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve,duration);
    })
}
async function foo(name){
    await sleep(2000)
    console.log(name)
}
async function foo2(){
    await foo("a");
    await foo("b");
}

foo2();

// 輸出順序:a(a等兩秒) b(b也等兩秒)
複製代碼

我的總結

看完這篇確實幫我解決了以前困擾個人問題,贊一個。。。明天也要加油鴨。。。。

相關文章
相關標籤/搜索