天天看點

Promise源碼解密-Promise A+标準注釋

系列文章

  1. Promise源碼解密-PromisesA+标準
  2. Promise源碼解密-同步版
  3. Promise源碼解密-異步版
  4. Promise源碼解密-then的鍊式調用
  5. Promise源碼解密-catch/resolve/reject/race

創作不易 拒絕白嫖 點個贊呗

關注專欄

Promise源碼解密 ,帶你走進Promise的深處!!!

術語

1. Promise

promise 是一個擁有

then

方法的 object 或 function ,其行為符合本規範

2. thenable

是一個定義了

then

方法的 object 或 function

3. 值(value)

指任何 JavaScript 的合法值(包括

undefined

, thenable 和 promise);

4. 異常(exception)

通過throw抛出一個值

5. 原因(reason)

一個promise被拒絕的原因

class Promise {
    constructor(executor) {
        this.value = undefined;
        this.reason = undefined;
        }    
 }

function Promise(){}
Promise.prototype.then=function(){}      

要求

1. Promise 的狀态

目前Promise的狀态隻能是下面三種狀态中的一種,不能存在多種狀态。

示例:

const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class Promise {
    constructor(executor) {
         this.status = STATUS_PENDING;
    }
}      

1.1. 等待(Pending)

處于等待時,狀态可以遷移至執行态或拒絕态

1.2. 執行(Fulfilled)

處于執行時:

  • 不能遷移至其他任何狀态
  • 必須擁有一個不可變的終值

1.3. 拒絕(Rejected)

處于拒絕時:

  • 必須擁有一個不可變的reason

這裡的不可變指的是恒等(即可用

===

判斷相等),引用資料類型隻要保證位址相等即可。

new Promise((resolve, reject) => {
        resolve('成功');
        reject('拒絕')
}).then((res) => {
    console.log(res,"res")
}, (error) => {
    console.log(error,'reason')

})

new Promise()時狀态是pending,
當程式執行,也就是(resolve, reject) => {
        resolve('成功');
        reject('拒絕')
},這個執行
這裡resolve把值進行了this.value = "成功";,但是并不會this.reason='拒絕',
這是因為隻有出現了執行就不會出現拒絕,最後輸出的結果是"成功 res",
如果注釋掉resolve('成功');,最後輸出的結果是 "拒絕 reason"。
            

2. Then 方法

一個 promise 必須提供一個

then

方法以通路其目前值、終值 和 reason。

promise 的

then

方法接受兩個函數參數:

promise.then(onFulfilled, onRejected)

then((res) => {
    console.log(res,"res")
}, (error) => {
    console.log(error,'reason')

})      

2.1

onFulfilled

onRejected

都是可選參數。

  • 如果

    onFulfilled

    不是函數,必須被忽略
  • onRejected

2.2

onFulfilled

onFulfilled

是函數:

  • promise

    執行結束後其必須被調用,其第一個參數為

    promise

    的value
  • promise

    執行結束前其不可被調用
  • 其調用次數不可超過一次
new Promise((resolve, reject) => {
 resolve(123)
}).then(result => {
    console.log(result);
}, error => {
    console.log(error);
});
// 這裡resolve執行結束才會執行then,這裡的result就是終值      

2.3 onRejected

onRejected

  • promise

    被拒絕執行後其必須被調用,其第一個參數為

    promise

    的reason
  • promise

    被拒絕執行前其不可被調用

2.4 調用時機

onFulfilled

onRejected

隻有在

執行環境

堆棧僅包含平台代碼時才可被調用 >> 注1

2.5 調用要求

onFulfilled

onRejected

必須被作為函數調用(即沒有

this

值)>> 注2

2.6 多次調用

then

方法可以被同一個

promise 調用多次

  • promise

    成功執行時,所有

    onFulfilled

    需按照其注冊順序依次回調
  • promise

    被拒絕執行時,所有的

    onRejected

new Promise.then().then()      

2.7 傳回

then

方法必須傳回一個

promise

對象 >> 注3, 這也就是then可以被多次調用的原因。

Promise.prototype.then=function(onFulfilled, onRejected){
    return new Promise()
}

promise2 = promise1.then(onFulfilled, onRejected);      
  • onFulfilled

    或者

    onRejected

    傳回一個值

    x

    ,則運作下面的 Promise 解決過程:

    [[Resolve]](promise2, x)

promise1.then((value)=>{
    return x;
}, (reason)=>{
    return x
}),
  // 這種情況需要處理x函數還是值還是其他情況 ,所有用到了  Promise 解決過程
  // 這種情況直接
Promise.prototype.then=function(onFulfilled, onRejected){
     return let promise2 = new Promise((resolve,reject)=>{
         try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
       } catch (e) {
           reject(e);
       }
    })
}      
  • onFulfilled

    onRejected

    抛出一個異常

    e

    ,則

    promise2

    必須拒絕執行,并傳回拒因

    e

promise1.then((value)=>{
    throw new Error()
}, (reason)=>{
    throw new Error()
}),
  // 這種情況直接
Promise.prototype.then=function(onFulfilled, onRejected){
     return new Promise((resolve,reject)=>{
         try {
           // console.log("執行 onFulfilled");
           //   這裡的x是啥? 是then中回調的return的傳回值
            onFulfilled(this.value);
           或者
            onReject(this.reason)
       } catch (e) {
           reject(e);
       }
    })
}
   
            
  • onFulfilled

    不是函數且

    promise1

    成功執行,

    promise2

    必須成功執行并傳回相同的值
promise1.then(1,2),

Promise.prototype.then=function(onFulfilled, onRejected){
     if (this.status === STATUS_FULFILLED) {
         return new Promise((resolve,reject)=>{
             if(typeof onFulfilled !== "function" ){
                 resolve(onFulfilled)
             }
             if(typeof onRejected !== "function" ){
                 resolve(onRejected)
             }
         })
     }
}      
  • onRejected

    promise1

    拒絕執行,

    promise2

    必須拒絕執行并傳回相同的reason
promise1.then(1,2),

Promise.prototype.then=function(onFulfilled, onRejected){
   if (this.status === STATUS_PENDING) {
         return new Promise((resolve,reject)=>{
            reject(onRejected)
         })
     }
}      

Promise 解決過程

Promise 解決過程是需要輸入一個 promise 和一個值 x 的一個抽象的操作,稱為

[[Resolve]](promise, x)

,如果

x

then

方法且看上去像一個 Promise ,解決程式即嘗試使

promise

接受

x

的狀态;否則其用

x

的值來執行

promise

。(x是傳回值,請注意上面的例子)

解決過程步驟:

x

promise

相等

promise

x

指向同一對象,直接 throw new TypeError

let p1=new Promise(resolve=>{
    resolve()
})
let p2=p1.then(data=>{
    // 傳回了p2
    return p2
})      

x

是Promise

x

是一個 Promise ,則使

promise

x

的狀态 >>注4:

  • x

    處于等待,

    promise

    需保持等待直至

    x

    被執行或拒絕
  • x

    處于執行,用相同的值執行

    promise

  • x

    處于拒絕,用相同的reason拒絕

    promise

x

為對象或函數

x

為對象或者函數:

  • x.then

    指派給

    then

    >> 注5
  • 如果取

    x.then

    的值時抛出錯誤

    e

    ,則以

    e

    為reason拒絕

    promise

  • 如果then是函數,将x作為函數的作用域this調用。傳遞兩個回調函數作為參數,第一個參數叫做resolvePromise,第二個參數叫做rejectPromise:
    • resolvePromise

      以值

      y

      為參數被調用,則運作

      [[Resolve]](promise, y)

    • rejectPromise

      以reason

      r

      為參數被調用,則以reason

      r

      拒絕

      promise

    • resolvePromise

      rejectPromise

      均被調用,或者被同一參數調用了多次,則優先采用首次調用并忽略剩下的調用
    • 如果調用then方法抛出了異常e:
      • resolvePromise

        rejectPromise

        已經被調用,則忽略之
  • 否則以

    e

    promise

    • then

      不是函數,以

      x

      為參數執行

      promise

  • x

    不為對象或者函數,以

    x

    promise

function resolvePromise(promise2, x, resolve, reject) {
    // 用來儲存是否已經reject或者resolve過
    let called
    if (promise2 === x) {
        throw new TypeError('Chaining cycle detected for promise')
    }
    // 如果是函數或者object的話先預設是promise
    if (x != null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            let then = x.then
            // 如果then是函數的話
            if (typeof then === 'function') {
                // 為啥不直接x.then()
                // 因為then已經判斷過是不是function,但是x.then沒有判斷過
                // 就讓then執行 第一個參數是this   後面是成功的回調 和 失敗的回調
                //  這裡的y是啥,如果x是promsie的話,那麼y就是x中的resolve/reject的值
                then.call(x, y => {
                    // 成功和失敗隻能調用一個

                    if (called) return;
                    called = true;
                    // resolve的結果依舊是promise 那就繼續解析
                    resolvePromise(promise2, y, resolve, reject);
                }, err => {
                    // 成功和失敗隻能調用一個
                    if (called) return;
                    called = true;
                    reject(err);
                })
            } else {
                resolve(x); // 如果不是函數,那就直接傳回結果
            }
        } catch (error) {
            // 成功和失敗隻能調用一個
            if (called) return;
            called = true;
            // 沒有then 不是函數也不是普通值
            reject(error)
        }
    } else {
        // x 是一個普通值
        resolve(x)
    }
}      

如果一個 promise 被一個循環的 thenable 鍊中的對象解決,而

[[Resolve]](promise, thenable)

的遞歸性質又使得其被再次調用,根據上述的算法将會陷入無限遞歸之中。算法雖不強制要求,但也鼓勵施者檢測這樣的遞歸是否存在,若檢測到存在則以一個可識别的

TypeError

為reason來拒絕

promise

>>注6。

注釋

  • 注1 這裡的平台代碼指的是引擎、環境以及 promise 的實施代碼。實踐中要確定

    onFulfilled

    onRejected

    方法異步執行,且應該在

    then

    方法被調用的那一輪事件循環之後的新執行棧中執行。這個事件隊列可以采用“宏任務(macro-task)”機制或者“微任務(micro-task)”機制來實作。由于 promise 的實施代碼本身就是平台代碼(**譯者注:**即都是 JavaScript),故代碼自身在處理在處理程式時可能已經包含一個任務排程隊列。
  • 注2 也就是說在**嚴格模式(strict)**中,函數

    this

    的值為

    undefined

    ;在非嚴格模式中其為全局對象。
  • 注3 代碼實作在滿足所有要求的情況下可以允許

    promise2 === promise1

    。每個實作都要文檔說明其是否允許以及在何種條件下允許

    promise2 === promise1

  • 注4 總體來說,如果

    x

    符合目前實作,我們才認為它是真正的 promise 。這一規則允許那些特例實作接受符合已知要求的 Promises 狀态。
  • 注5 這裡我們存儲了x.then的引用 ,然後在if中進行判斷,并且可以避免多次通路

    x.then

    屬性。同時這確定了該屬性的一緻性,因為其值可能在檢索調用時被改變。
  • 注6 實作不應該對 thenable 鍊的深度設限,并假定超出本限制的遞歸就是無限循環。隻有真正的循環遞歸才應能導緻

    TypeError

    異常;如果一條無限長的鍊上 thenable 均不相同,那麼遞歸下去永遠是正确的行為。

繼續閱讀