如何手寫一個(gè)Promise

參考文章:《Promise,從入門到放棄》

為了方便比較與原裝的Promise區(qū)別,手寫的Promise被命名為iPromise。

實(shí)現(xiàn)本體

  1. 首先,Promise是一個(gè)類,接收一個(gè)函數(shù)executor作為構(gòu)造函數(shù),該函數(shù)接受兩個(gè)函數(shù)作為參數(shù):resolverejectPromise自帶,并不需要使用者手動(dòng)部署),且立即(同步)執(zhí)行。

Promise對(duì)象的promiseResult屬性存儲(chǔ)執(zhí)行的結(jié)果,promiseState屬性存儲(chǔ)狀態(tài)。
Promise有三種狀態(tài):pending(準(zhǔn)備中)、fulfilled(滿足)和rejected(拒絕)。初始狀態(tài)是pending

class iPromise {
  constructor(executor) {
    // 存儲(chǔ)promise結(jié)果
    this.promiseResult = undefined;
    // 存儲(chǔ)promise的狀態(tài)
    this.promiseState = 'pending';
    
    // 立即執(zhí)行executor
    executor(resolve, reject);
  }

}
  1. resolve方法的作用是將執(zhí)行所得的結(jié)果賦值給promiseResult,并將promiseStatepending變?yōu)?code>fulfilled。reject方法基本一樣,只是將promiseStatepending變?yōu)?code>rejected。

promise對(duì)象的狀態(tài)變化只能變化一次;
executor執(zhí)行出現(xiàn)錯(cuò)誤,也會(huì)觸發(fā)reject函數(shù)。

class iPromise {
  constructor(executor) {
    // 存儲(chǔ)promise結(jié)果
    this.promiseResult = undefined;
    // 存儲(chǔ)promise的狀態(tài)
    this.promiseState = 'pending';
    
    // resolve方法,將promiseState變?yōu)閒ulfilled,并修改promiseResult
    const resolve = (value) => {
      // 僅在promiseState為pending的時(shí)候變化
      if (this.promiseState !== 'pending') return;
      // 將promiseState變?yōu)閒ulfilled
      this.promiseState = 'fulfilled';
      // 將value作為promiseResult
      this.promiseResult = value;
    }
    
    // reject方法,將promiseState變?yōu)閞ejected,并修改promiseResult
    const reject = (error) => {
      // 僅在promiseState為pending的時(shí)候變化
      if (this.promiseState !== 'pending') return;
      // 將promiseState變?yōu)閞ejected
      this.promiseState = 'rejected';
      // 將error作為promiseResult
      this.promiseResult = error;
    }
    
    // 立即執(zhí)行executor
    // executor函數(shù)執(zhí)行出現(xiàn)錯(cuò)誤,會(huì)調(diào)用reject方法
    try {
      executor(resolve, reject);
    } catch (error) {
        reject(error);
    }
  }

}

實(shí)現(xiàn)then方法

  1. Promise.then()方法接收1~2個(gè)回調(diào)函數(shù)作為參數(shù),返回一個(gè)新的Promise對(duì)象(下文中如果沒有特殊說明,Promise對(duì)象都指原Promise對(duì)象)以支持鏈?zhǔn)秸{(diào)用。

返回的新Promise對(duì)象的參數(shù)必須使用箭頭函數(shù)()=>{},否則會(huì)造成this指向錯(cuò)誤的問題。當(dāng)然,你也可以采取let self = this;存儲(chǔ)this然后傳入的形式,不過有點(diǎn)多此一舉。

class iPromise {
  constructor(executor){
    // 構(gòu)造函數(shù)
  }
  
  // 接收兩個(gè)回調(diào)函數(shù)作為參數(shù)
  then(onResolved, onRejected) {
    /*
    * 這里必須要寫箭頭函數(shù),否則this會(huì)指向新的Promise對(duì)象
    * 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
    */ 
    return new iPromise((resolve, reject) => {
      // ...
    })
  }
}
  1. 根據(jù)promiseResult的值不同,分為兩種情況:當(dāng)其為Promise對(duì)象時(shí),遞歸執(zhí)行它的then方法;當(dāng)其為其他類型,直接調(diào)用resolve方法。
class iPromise {
  constructor(executor){
    // ...
  }
  
  // 接收兩個(gè)回調(diào)函數(shù)作為參數(shù)
  then(onResolved, onRejected) {
    /*
    * 這里必須要寫箭頭函數(shù),否則this會(huì)指向新的Promise對(duì)象
    * 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
    */ 
    return new iPromise((resolve, reject) => {
      
      /*
      * 回調(diào)處理函數(shù)
      * 這里也請記得用箭頭函數(shù),this要穿透幾層
      * 箭頭函數(shù)就用幾層
      */
      const handleCallback = (callback) => {
        try {
            let res = callback(this.promiseResult);
            // 若返回值是promise對(duì)象
            if (res instanceof Promise) {
              res.then(val => resolve(val), err => reject(err));
            } else {
              // 若不是
              resolve(res);
            }
        } catch (error) {
          reject(error);
        }
      }
    })
  }
}
  1. 根據(jù)promiseState的值來確定應(yīng)該執(zhí)行哪個(gè)回調(diào)函數(shù):
class iPromise {
  constructor(executor){
    // ...
  }
  
  // 接收兩個(gè)回調(diào)函數(shù)作為參數(shù)
  then(onResolved, onRejected) {
    /*
    * 這里必須要寫箭頭函數(shù),否則this會(huì)指向新的Promise對(duì)象
    * 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
    */ 
    return new iPromise((resolve, reject) => {
      
      /*
      * 回調(diào)處理函數(shù)
      * 這里也請記得用箭頭函數(shù),this要穿透幾層
      * 箭頭函數(shù)就用幾層
      */
      const handleCallback = (callback) => {
        try {
            let res = callback(this.promiseResult);
            // 若返回值是promise對(duì)象
            if (res instanceof Promise) {
              res.then(val => resolve(val), err => reject(err));
            } else {
              // 若不是
              resolve(res);
            }
        } catch (error) {
          reject(error);
        }
      }
      
      // promiseState為fulfilled時(shí)調(diào)用onResolved
      if (this.promiseState === "fulfilled") {
        handleCallback(onResolved);
      }
      
      // promiseState為rejected時(shí)調(diào)用onRejected
      if (this.promiseState === "rejected") {
        handleCallback(onRejected);
      }
    })
  }
}
  1. 因?yàn)楫惒饺蝿?wù)的問題,并且支持多個(gè)回調(diào),所以我們需要對(duì)回調(diào)函數(shù)采用數(shù)組進(jìn)行存儲(chǔ),所以引入了新的變量:callbackList
class iPromise {
  constructor(executor) {
    // 存儲(chǔ)promise結(jié)果
    this.promiseResult = undefined;
    // 存儲(chǔ)promise的狀態(tài)
    this.promiseState = 'pending';
    // 存儲(chǔ)所有的回調(diào)函數(shù)
    this.callbackList = [];
    
    // resolve方法,將promiseState變?yōu)閒ulfilled,并修改promiseResult
    const resolve = (value) => {
      // 僅在promiseState為pending的時(shí)候變化
      if (this.promiseState !== 'pending') return;
      // 將promiseState變?yōu)閒ulfilled
      this.promiseState = 'fulfilled';
      // 將value作為promiseResult
      this.promiseResult = value;
      // 異步執(zhí)行所有回調(diào)函數(shù)
      this.callbackList.forEach(cb => cb.onResolved(value));
    }
    
    // reject方法,將promiseState變?yōu)閞ejected,并修改promiseResult
    const reject = (error) => {
      // 僅在promiseState為pending的時(shí)候變化
      if (this.promiseState !== 'pending') return;
      // 將promiseState變?yōu)閞ejected
      this.promiseState = 'rejected';
      // 將error作為promiseResult
      this.promiseResult = error;
      // 異步執(zhí)行所有回調(diào)函數(shù)
      this.callbackList.forEach(cb => cb.onRejected(error));
    }
    
    // 立即執(zhí)行executor
    // executor函數(shù)執(zhí)行出現(xiàn)錯(cuò)誤,會(huì)調(diào)用reject方法
    try {
      executor(resolve, reject);
    } catch (error) {
        reject(error);
    }
  }
  
  // 接收兩個(gè)回調(diào)函數(shù)作為參數(shù)
  then(onResolved, onRejected) {
    /*
    * 這里必須要寫箭頭函數(shù),否則this會(huì)指向新的Promise對(duì)象
    * 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
    */ 
    return new iPromise((resolve, reject) => {
      
      /*
      * 回調(diào)處理函數(shù)
      * 這里也請記得用箭頭函數(shù),this要穿透幾層
      * 箭頭函數(shù)就用幾層
      */
      const handleCallback = (callback) => {
        try {
            let res = callback(this.promiseResult);
            // 若返回值是promise對(duì)象
            if (res instanceof Promise) {
              res.then(val => resolve(val), err => reject(err));
            } else {
              // 若不是
              resolve(res);
            }
        } catch (error) {
          reject(error);
        }
      }
      
      // promiseState為fulfilled時(shí)調(diào)用onResolved
      if (this.promiseState === "fulfilled") {
        handleCallback(onResolved);
      }
      
      // promiseState為rejected時(shí)調(diào)用onRejected
      if (this.promiseState === "rejected") {
        handleCallback(onRejected);
      }
      
      /*
       * 如果是pending狀態(tài),則異步任務(wù),在改變狀態(tài)的時(shí)候去調(diào)用回調(diào)函數(shù)
       * 所以要保存回調(diào)函數(shù)
       * 因?yàn)閜romise實(shí)例可以指定多個(gè)回調(diào),于是采用數(shù)組 
       */
      if (this.promiseState === "pending") {
        this.callbackList.push({
            onResolved: () => {
              handleCallback(onResolved)
            },
            onRejected: () => {
              handleCallback(onRejected)
            }
        })
      }
    })
  }
}

catch方法

catch方法主要需要做到的就是異常穿透:

當(dāng)使用promisethen進(jìn)行鏈?zhǔn)秸{(diào)用時(shí),可以在最后指定失敗的回調(diào)。前面的任何錯(cuò)誤都會(huì)在最后傳到失敗的回調(diào)中去處理,除非在中途被失敗回調(diào)函數(shù)(onRejected)處理了。

// promise對(duì)象的異常穿透
let p1 = Promise.resolve(1);
p1.then((value)=>{
    console.log(11);
}).then((value)=>{
    throw 'err';
}).then((value)=>{
    console.log(22);
}).catch(err=>{
    console.log(err);
})

// 最終輸出:
// 11
// err

要實(shí)現(xiàn)catch,我們可以直接調(diào)用iPromise.then方法,但不傳入onResolve方法。

同時(shí),我們需要給then中的onResolveonRejected賦初始值,順便避免了傳入undefined或其他非函數(shù)值而報(bào)錯(cuò):

class iPromise {
  constructor(executor) {
    // 構(gòu)造函數(shù)
  }
  
  // 接收兩個(gè)回調(diào)函數(shù)作為參數(shù)
  then(onResolved, onRejected) {
  
    // 處理異常穿透,并設(shè)置默認(rèn)值以避免程序出錯(cuò)
    if(typeof onResolved !== 'function') {
      onResolve = (val) => val;
    }
    
    if(typeof onRejected !== 'function') {
      onRejected = (err) => {
        throw err;
      }
    }
    
    return new iPromise((resolve, reject) => {
      // ...調(diào)用回調(diào)函數(shù)
    })
  }
  
  // catch方法
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
}

Promise.resolve方法

Promise.resolve方法返回成功或者失敗的Promise對(duì)象。如果傳入的參數(shù)為非Promise類型的對(duì)象,則返回的結(jié)果為成功的Promise對(duì)象。如果傳入的參數(shù)為Promise對(duì)象,則參數(shù)Promise返回的結(jié)果就是 Promise.resolve返回的結(jié)果。

let promiseA = Promise.resolve(1);

// 比如這時(shí)return 一個(gè)[[PromiseResult]]的值為err的Promise對(duì)象。
let PromiseB = Promise.resolve(new Promise((resolve,reject)=>{
    reject('err');
})

實(shí)現(xiàn)它,我們需要用到靜態(tài)方法

class iPromise {
  constructor(executor) {
    // 構(gòu)造函數(shù)
  }
  
  // ...其他方法
  
  // 靜態(tài)方法只能通過類本身來調(diào)用
  static resolve(value) {
    return new iPromise((resolve, reject) => {
      // 如果是iPromise對(duì)象
      if (value instanceof iPromise) {
        value.then(val => resolve(val), err => reject(err));
      } else {
        resolve(value);
      }
    })
  }
}

Promise.reject方法

Promise.reject方法返回一個(gè)失敗的Promise對(duì)象,promiseResult的值為Promise.reject參數(shù)。

有多失敗?大概像我一樣失敗。

let PromiseA = Promise.reject(new Promise((resolve,reject)=>{
    resolve('err');
})
// 無論傳入是啥,就返回一個(gè)失敗的Promise對(duì)象,[[PromiseResult]]的值為 Promise.reject的參數(shù)

實(shí)現(xiàn):

class iPromise {
  constructor(executor) {
    // 構(gòu)造函數(shù)
  }
  
  // ...其他方法
  
  // 靜態(tài)方法只能通過類本身來調(diào)用
  static reject(error) {
    return new iPromise((resolve, reject) => {
      reject(error);
    })
  }
}

Promise.all方法

Promise.all方法接收的參數(shù)是由n個(gè)Promise對(duì)象的數(shù)組。返回新的Promise對(duì)象,只有所有的Promise對(duì)象都成功才成功,返回的對(duì)象的promiseResult為包含所有Promise對(duì)象的數(shù)組。只要有一個(gè)失敗了就直接失敗,返回的對(duì)象的promiseResult為失敗的Promise對(duì)象的執(zhí)行結(jié)果。

class iPromise {
  constructor(executor) {
    // 構(gòu)造函數(shù)
  }
  
  // ...其他方法
  
  static all(promiseArrays) {
    return new iPromise((resolve, reject) => {
      // 用以存儲(chǔ)執(zhí)行的結(jié)果
      let results = [];
      let length  = promiseArrays.length;
      promiseArrays.forEach((promiseObj, index, promiseArrays) => {
        promiseObj.then((val) => {
          results.push(val);
          // 由于是多個(gè)異步任務(wù)的關(guān)系,需要判斷是否都執(zhí)行完畢
          if (results.length === length) {
            resolve(results);
          }
        }, err => {
          // 如有錯(cuò)誤,則reject
          reject(err);
        });
      })
    })
  }
  
}

Promise.race方法

Promise.race方法接收的參數(shù)是由n個(gè)Promise對(duì)象的數(shù)組。返回新的Promise對(duì)象,第一個(gè)完成的Promise的結(jié)果狀態(tài)就是最終結(jié)果的狀態(tài)。

你可能會(huì)問:那不鐵定第一個(gè)Promise對(duì)象是第一個(gè)完成的嗎?
這是因?yàn)槲覀兊淖钪匾墓δ苓€沒做:異步。

class iPromise {
  constructor(executor) {
    // 構(gòu)造函數(shù)
  }
  
  // ...其他方法
  
  // race方法
  static race(promiseArrays) {
    return new iPromise((resolve, reject) => {
      promiseArrays.forEach(promiseObj => {
        promiseObj.then(val => {
          resolve(val);
        }, err => {
          reject(err);
        });
      })
    })
  }
  
}

加點(diǎn)細(xì)節(jié)—由同步到異步

使用setTimeout將其變?yōu)楫惒饺蝿?wù)。

setTimeout只能將任務(wù)變更為宏觀異步任務(wù)。原裝的Promise是微觀異步任務(wù)。

class iPromise {

  constructor(executor) {
    // 存儲(chǔ)promise結(jié)果
    this.promiseResult = undefined;
    // 存儲(chǔ)promise的狀態(tài)
    this.promiseState = 'pending';
    // 存儲(chǔ)所有的回調(diào)函數(shù)
    this.callbackList = [];
    
    // resolve方法,將promiseState變?yōu)閒ulfilled,并修改promiseResult
    const resolve = (value) => {
      // 僅在promiseState為pending的時(shí)候變化
      if (this.promiseState !== 'pending') return;
      // 將promiseState變?yōu)閒ulfilled
      this.promiseState = 'fulfilled';
      // 將value作為promiseResult
      this.promiseResult = value;
      // 異步執(zhí)行所有回調(diào)函數(shù)
      setTimeout(()=>{
        this.callbackList.forEach(cb => cb.onResolved(value));
      })
    }
    
    // reject方法,將promiseState變?yōu)閞ejected,并修改promiseResult
    const reject = (error) => {
      // 僅在promiseState為pending的時(shí)候變化
      if (this.promiseState !== 'pending') return;
      // 將promiseState變?yōu)閞ejected
      this.promiseState = 'rejected';
      // 將error作為promiseResult
      this.promiseResult = error;
      // 異步執(zhí)行所有回調(diào)函數(shù)
      setTimeout(()=>{
        this.callbackList.forEach(cb => cb.onRejected(error));
      })
    }
    
    // 立即執(zhí)行executor
    // executor函數(shù)執(zhí)行出現(xiàn)錯(cuò)誤,會(huì)調(diào)用reject方法
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  // 接收兩個(gè)回調(diào)函數(shù)作為參數(shù)
  then(onResolved, onRejected) {
    //處理異常穿透并且為onResolved,onRejected設(shè)置默認(rèn)值。因?yàn)檫@兩個(gè)參數(shù)可以都不傳
    if (typeof onRejected !== 'function') {
      onRejected = err => {
        throw err;
      }
    }
    if (typeof onResolved !== 'function') {
      onResolved = val => val;
    }
    /*
    * 這里必須要寫箭頭函數(shù),否則this會(huì)指向新的Promise對(duì)象
    * 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
    */
    return new iPromise((resolve, reject) => {
      /*
      * 回調(diào)處理函數(shù)
      * 這里也請記得用箭頭函數(shù),this要穿透幾層
      * 箭頭函數(shù)就用幾層
      */
      const handleCallback = (callback) => {
        try {
          let res = callback(this.promiseResult);
          // 若返回值是promise對(duì)象
          if (res instanceof iPromise) {
            res.then(val => resolve(val), err => reject(err));
          } else {
            // 若不是
            resolve(res);
          }
        } catch (error) {
          reject(error);
        }
      }
      // promiseState為fulfilled時(shí)調(diào)用onResolved
      if (this.promiseState === "fulfilled") {
        setTimeout(() => {
          handleCallback(onResolved);
        });
      }
      // promiseState為rejected時(shí)調(diào)用onRejected
      if (this.promiseState === "rejected") {
        setTimeout(() => {
          handleCallback(onRejected);
        });
      }
      /*
      * 如果是pending狀態(tài),則異步任務(wù),在改變狀態(tài)的時(shí)候去調(diào)用回調(diào)函數(shù)
      * 所以要保存回調(diào)函數(shù)
      * 因?yàn)閜romise實(shí)例可以指定多個(gè)回調(diào),于是采用數(shù)組 
      */
      if (this.promiseState === "pending") {
        this.callbackList.push({
          onResolved: () => {
            handleCallback(onResolved)
          },
          onRejected: () => {
            handleCallback(onRejected)
          }
        })
      }
    })
  }

  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

  static resolve(value) {
    return new iPromise((resolve, reject) => {
      if (value instanceof iPromise) {
        value.then(val => resolve(val), err => reject(err));
      } else {
        resolve(value)
      }
    })
  }

  static reject(error) {
    return new iPromise((resolve, reject) => {
      reject(error);
    })
  }

  static all(promiseArrays) {
    return new iPromise((resolve, reject) => {
      // 用以存儲(chǔ)執(zhí)行的結(jié)果
      let results = [];
      let length  = promiseArrays.length;
      promiseArrays.forEach((promiseObj, index, promiseArrays) => {
        promiseObj.then((val) => {
          results.push(val);
          // 由于是多個(gè)異步任務(wù)的關(guān)系,需要判斷是否都執(zhí)行完畢
          if (results.length === length) {
            resolve(results);
          }
        }, err => {
          // 如有錯(cuò)誤,則reject
          reject(err);
        });
      })
    })
  }

  static race(promiseArrays) {
    return new iPromise((resolve, reject) => {
      promiseArrays.forEach(promiseObj => {
        promiseObj.then(val => {
          resolve(val);
        }, err => {
          reject(err);
        });
      });
    })
  }
}

總結(jié)

......沒啥好說的,從上到下敲一遍,不說運(yùn)用自如,怎么也得是一臉懵逼吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容