關於一道promise的詭異題目的理解

要解決的題目以下:面試

Promise.resolve().then(() => {
    console.log(0);
    return Promise.resolve(4);
}).then((res) => {
    console.log(res)
})

Promise.resolve().then(() => {
    console.log(1);
}).then(() => {
    console.log(2);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(5);
}).then(() =>{
    console.log(6);
})
複製代碼

輸出結果按順序爲0,1,2,3,4,5,6chrome

一、分析

通常遇到Promise.resolve()時,至關於new Promise(resolve => {resolve()})都是同步完成的,不會消耗微任務。 但如下狀況時,須要注意,咱們先看三組代碼:瀏覽器

//代碼1
new Promise(resolve => {
    resolve(Promise.resolve(4));//resolve了一個Promise
})
.then((res) => {
    console.log(res)
})
複製代碼
//代碼2
Promise.resolve().then(() => {
    return Promise.resolve(4);//return了一個Promise
})
.then((res) => {
    console.log(res)
})
複製代碼
//代碼3
Promise.resolve().then(() => {
    return 4;//return了一個Number類型的4
})
.then((res) => {
    console.log(res)
})
複製代碼

這三個輸出結果,打印出來的都是數字4。微信

咱們能夠看出不一樣,代碼3是咱們最多見的狀況。代碼3裏打印的res是4,和上邊return的是一樣的數據類型。那麼代碼1和代碼2的res爲何不是Object類型的Promise{<fulfilled>: 4}呢?markdown

在通常狀況下:函數

Promise.resolve().then(() => {
    return 4;
})
複製代碼

這段代碼中,Promise.resolve().then是一個構造函數,() => {return 4;}是這個函數的參數,這個函數調用,最後返回一個值爲4Promise(即new Promise(resolve => resolve(4)).post

而在ui

new Promise(resolve => {
    resolve(Promise.resolve(4));//resolve了一個Promise
})
複製代碼
Promise.resolve().then(() => {
    return Promise.resolve(4);//return了一個Promise
})
複製代碼

中,由於js在遇到resolve或者return一個Promise對象時,會先求得這個Promise對象的值,也就是這個Promise的狀態爲fulfilledrejected的值(假如這個值是'a'),再用這個值做爲返回的新的Promised的值,這個新的Promsie(就是new Promise(resolve => resolve('a'))做爲下級鏈式調用的Promisespa

二、結論

在chrome內部實現的Promise和標準的Promise/A+規範存在差別。瀏覽器內部實現的區別。咱們能夠理解爲,resolve或者return遇到一個Promise對象時,獲得這個Promise的值以後,會把這個值用微任務包裝起來,在return值向外傳遞(由於後邊沒有.then()了,因此是向父級的外層傳遞)時,會產生第二個微任務。code

因此代碼

//代碼1
new Promise(resolve => {
    resolve(Promise.resolve(4));//resolve了一個Promise
})
.then((res) => {
    console.log(res)
})
複製代碼

能夠理解爲

new Promise(resolve => {
    resolve(4);
})
.then()
.then()
.then((res) => {
    console.log(res)
})
複製代碼

對應的,代碼

//代碼2
Promise.resolve().then(() => {
    return Promise.resolve(4);//return了一個Promise
})
.then((res) => {
    console.log(res)
})
複製代碼

能夠理解爲

Promise.resolve()
.then(() => {
    return 4;
})
.then()
.then()
.then((res) => {
    console.log(res)
})
複製代碼

這樣理解的,和文章開頭的題目結果是一致的,核心就是會比正常的return一個非Promise的值時,多兩個微任務.then().then()

另外的

Promise.resolve().then(() => {
    return Promise.resolve(Promise.resolve(Promise.resolve(4)))
})
.then(res => {
    console.log(res);
})
複製代碼

像這樣的return Promise.resolve(Promise.resolve(Promise.resolve(4)))嵌套多層Promise,其實和Promise.resolve(4)是同樣的,並不會多產生微任務。由於這兩段代碼的Promsie狀態變爲fulfilled的過程並不須要等待。而是拿到它的值以後,在向後運行的時候,會產生微任務。

但若是是

Promise.resolve().then(() => {
    return new Promise(resolve => {
            resolve(4)
    })
    .then(res => {
            return 4.1
    })
    .then(res => {
            return 4.2
    })
})
.then(res => {
    console.log(res);
})
複製代碼

這時.then(res => { console.log(res); })想要運行,須要等待前邊return 的Promise狀態變爲fulfilled才行,

new Promise(resolve => {
        resolve(4)
})
.then(res => {
        return 4.1
})
.then(res => {
        return 4.2
})
複製代碼

自己是會註冊兩個微任務的,而拿到它的值以後,在向後運行的時候,又會產生兩個任務(包裝值一次,return傳遞一次)。

三、回顧

咱們來回顧下文章開頭的題目

Promise.resolve().then(() => {
    console.log(0);
    return Promise.resolve(4);
}).then((res) => {
    console.log(res)
})

Promise.resolve().then(() => {
    console.log(1);
}).then(() => {
    console.log(2);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(5);
}).then(() =>{
    console.log(6);
})
複製代碼

按照上邊的分析,能夠對應轉化爲

Promise.resolve().then(() => {
    console.log(0);
    return 4;
})
.then()
.then()
.then((res) => {
    console.log(res)
})

Promise.resolve().then(() => {
    console.log(1);
}).then(() => {
    console.log(2);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(5);
}).then(() =>{
    console.log(6);
})
複製代碼

運行結果都是0,1,2,3,4,5,6

題目2

Promise.resolve().then(() => {
    return new Promise(resolve => {
         resolve(4)
    })
    .then(res => {
         console.log(res, 'then4_1');
         return 4.1
    })
    .then(res => {
         console.log(res);
         return 4.2
    })
})
.then(res => {
   console.log(res, 'then4_2');
})

Promise.resolve().then(() => {
    console.log(1);
}).then(() => {
    console.log(2);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(5);
}).then(() =>{
    console.log(6);
})
複製代碼

運行結果爲

微信圖片_20210411090309.png 題目3

Promise.resolve().then(() => {
    console.log(0);
    return Promise.resolve(Promise.resolve(Promise.resolve(4)));
}).then((res) => {
    console.log(res)
})

Promise.resolve().then(() => {
    console.log(1);
}).then(() => {
    console.log(2);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(5);
}).then(() =>{
    console.log(6);
})
複製代碼

運行結果0,1,2,3,4,5,6

參考文章

深度揭祕 Promise 微任務註冊和執行過程

從一道讓我失眠的 Promise 面試題開始,深刻分析 Promise 實現細節

相關文章
相關標籤/搜索