番外:深入理解Promise - 03 Promise的幾個重要問題

snakeZix發表於2021-01-02

Promise的幾個重要問題

一、 Promise什麼時候發生狀態改變?什麼時候獲取值?

前面說過Promise的excutor是同步執行的回撥函式,但是可以觸發非同步任務,因此有兩種情況:

  • 在excutor執行器中立即同步執行呼叫敲定函式,於是同步的例項改變狀態並且獲取到結果

  • 在excutor執行器中非同步的執行呼叫敲定函式,比如setTimeout,於是非同步的改變例項狀態並獲取結果

二、Promise的例項什麼一定要改變狀態前指定回撥函式嗎?

不!Promise的例項可以在任何時候指定或者說繫結回撥函式

  • 改變前:

    const promise = new Promise((fulfill, reject) => {
        setTimeout(()=>{
            fulfill(true)
        }, 1000)
    })
    promise.then(onFulfilled, onRejected)
    
  • 改變後:
    同步的改變狀態

    const promise = new Promise((fulfill, reject) => {
        fulfill(true)
    })
    promise.then(onFulfilled, onRejected)
    

    或者,非同步的繫結回撥

    const promise = new Promise((fulfill, reject) => {
        setTimeout(()=>{
            fulfill(true)
        }, 1000)
    })
    setTimeout(()=>{
            promise.then(onFulfilled, onRejected)
    }, 2000)
    

三、 Promise的例項可以繫結多個then還是隻能繫結最後一個?

可以繫結多個,一旦promise例項的狀態發生轉變,按照繫結順序依次呼叫

const promise = new Promise((fulfill, reject) => {
    setTimeout(()=>{
        fulfill(true)
    }, 1000)
})
promise.then(onFulfilled1, onRejected1)
promise.then(onFulfilled2, onRejected2)

最終 onFulfilled1 和 onFulfilled2 依次執行

四、 excutor 執行器 和 then 的回撥函式的關係

我們先整理一下,什麼操作能產生Promise例項

  • new Prmoise(excutor)
  • Prmoise.resolve
  • Prmoise.reject
  • Prmoise的例項呼叫then等方法
  • promise.all、any等靜態方法 這個單獨考慮

我們對齊分類:

  • promise.all、any,對多個promise進行處理,併發或者競爭

  • 使用 excutor ,能夠實現從0到有,即new Prmoise(excutor),Prmoise.resolve和Prmoise.reject只不過是對其的簡寫,特別是thenable物件的then幾乎和 excutor 一模一樣

  • then等方法,它顯得極為特殊,是在原有的promise新生成一個promise(注意原來的Promise例項每呼叫生成的新的Promise例項都是不同的)

但是如果你對then的結構、行為、效果仔細分析,它其實也是某種意義上的 excutor,我們假設一個場景:

const promise = new Promise((fulfill, reject) => {
    setTimeout(()=>{
        fulfill(true)
    }, 1000)
})
promise.then(onFulfilled, onRejected)

根據promise.then的效果寫一些虛擬碼:

/*
當事件輪詢到執行Promise回撥時

if 當前的Promise例項狀態發生了改變 {
    if(改變的狀態為 fulfilled) {
        執行onFulfilled函式
    }else if(改變的狀態為 rejected) {
        執行onRejected函式
    }
    根據狀態處理函式結果返回 新的 Promise例項
}
*/

功能上,then的兩個處理函式 是對 excutor 的分解,onFulfilled的執行不僅僅是處理當前Promise例項的值,更是由其返回的值敲定新的Promise的狀態並儲存結果,onRejected也是如此。
對於返回的新的Promise例項其內部就像執行如下函式

  • fullfill(onFulfilledResult)
  • reject(onRejectedResult)

看下面這段程式碼

let promise = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve(new Promise((resolve, reject) => {
            setTimeout(()=>{
                resolve(1)
                console.log('內層promise')
            }, 1000)
        }))
        console.log('外層promise')
    }, 1000)
}).then((value)=>{
    console.log(value)
})

這裡的resolve接受引數返回的新的Promise例項和then的onFulfilled呼叫返回值決定的新的Promise例項是一致的

五、串聯多個Promise任務

只要在excutor執行器上敲定函式的實參 或者 then的回撥函式返回值 指定的需要串聯的Promise例項,返回的新的Promise就會"跟隨"傳入該例項

六、異常穿透 => catch

let onFulfilled1 = function(value) {
    console.log(value)
}
let onFulfilled2 = onFulfilled1
let onFulfilled3 = onFulfilled2


let onRejected = function(error) {
    console.log(error, '錯誤獲取')
    console.log(promise2 === promise1)
}
const promise = new Promise((fulfill, reject) => {
    setTimeout(()=>{
        reject(true)
    }, 1000)
})

const promise1 = promise.then()
const promise2 = promise1.then()

promise2.then(onFulfilled3)
.catch(onRejected)

如果當前呼叫then方法的Promise例項已經發生狀態變化,並返回結果,但是找不到then傳入的處理對應的處理函式,並不會丟失,而是交給新返回的Promise例項使得新的例項的then能夠處理到上一個例項的結果。
或者說,如果then函式沒有傳入當前Promise例項對應狀態的處理函式,那麼預設返回一個和當前Promise例項狀態相同,結果相同的新的Promise

七、中斷Promise鏈

只要Promise的例項狀態不敲定,那麼then的回撥不會執行

let promise
promise = new Promise((fulfill, reject) => {
    setTimeout(()=>{
        reject(true)
    }, 1000)
})
promise.then(
    value => {
        return new Promise((fulfill, reject) => {})//返回未敲定的Promise例項
    },
    errorReason => {
        return new Promise((fulfill, reject) => {})//返回未敲定的Promise例項
    }
)

//或者

promise = new Promise((fulfill, reject) => {
    setTimeout(()=>{
        reject(true)
    }, 1000)
})
promise.finally(()=>{
    return new Promise((fulfill, reject) => {})//返回未敲定的Promise例項
})

相關文章