手書きPromise——SE 6によるPromise実現(詳細コメント含む)

7930 ワード

1.Promise A+仕様
  • 公式英語アドレス:https://promisesaplus.com/
  • 中国語の翻訳は参考にできます。http://malcolmyu.github.io/malnote/2015/06/12/Promises-A-Plus/
  • 2.コード実現
    githubアドレス:https://github.com/whu-luojian/Promise.git
    
    // promise     
    
    const STATUS = {
    
      PENDING: 0,
    
      FULFILLED: 1,
    
      REJECTED: 2
    
    }
    
    class Promise {
    
      constructor(task) {    
    
        // promise    
    
        this.status = STATUS.PENDING;
    
        // resolve      
    
        this.resolveData = null;
    
        // reject      
    
        this.rejectData = null;
    
        // resolve reject        
    
        // promise resolve reject      ,   then promise 
    
        // pending  ,    then        ,  promise resolve 
    
        // reject      
    
        this.onFulfilledList = [];
    
        this.onRejectedList = [];
    
        /**
    
        * promise  ,  onFulfilledList  
    
        * @param {*} data 
    
        */
    
        this.onResolve = (data) => {
    
          if(this.status === STATUS.PENDING) {
    
            this.status = STATUS.FULFILLED;
    
            this.resolveData = data;
    
            this.onFulfilledList.forEach(fn => {
    
              fn(this.resolveData)
    
            })
    
          }
    
        }
    
        /**
    
        * promise  ,  onRejectedList  
    
        * @param {*} err 
    
        */
    
        this.onReject = (err) => {
    
          if(this.status === STATUS.PENDING) {
    
            this.status = STATUS.REJECTED;
    
            this.rejectData = err;
    
            this.onRejectedList.forEach(fn => {
    
              fn(this.rejectData)
    
            })
    
          }
    
        }
    
        /**
    
        * promise  ,   then              promise
    
        *   ,    then      Promise thenable   
    
        * @param {*  then return  } data 
    
        * @param {*  then resolve} resolve 
    
        * @param {*  then reject} reject 
    
        */
    
        this.resolvePromise = (data, resolve, reject) => {
    
          // then return       promise
    
          if(data instanceof Promise) {
    
            if(data.status === STATUS.PENDING) {
    
              data.then((val) => {
    
                this.resolvePromise(val, resolve, reject);
    
              }, reject)
    
            } else if (data.status === STATUS.FULFILLED) {
    
              resolve(data.resolveData)
    
            } else {
    
              reject(data.rejectData)
    
            }
    
          } 
    
          // then return      ,     then  ,           then
    
          // Promise thenable     
    
          else if(data !== null && data instanceof Object) {
    
            try {
    
              let then = data.then
    
              if(then instanceof Function) {
    
                then.call(data, (val) => {
    
                  this.resolvePromise(val, resolve, reject);
    
                }, reject)
    
              } else {
    
                resolve(data)
    
              }
    
            } catch (err) {
    
              reject(err)
    
            }
    
          }
    
          // then return        undefined
    
          else {
    
            resolve(data)
    
          }
    
        }
    
        //        task
    
        try {
    
          task(this.onResolve.bind(this), this.onReject.bind(this))
    
        } catch (err) {
    
          this.onReject(err)
    
        }
    
      }
    
      /**
    
      * then  ,    promise
    
      *   :  then         ,    ,      promise   resolve reject  
    
      * promise   ,  then        ,      promise           , 
    
      *             promise   
    
      * @param {*  } onFulfilled 
    
      * @param {*  } onRejected 
    
      */
    
      then(onFulfilled, onRejected) {
    
        let promise;
    
        // pending      then     promise       
    
        //   promise       
    
        if(this.status === STATUS.PENDING) {
    
          promise = new Promise((resolve, reject) => {
    
            this.onFulfilledList.push(() => {
    
              //   then          
    
              if(!(onFulfilled instanceof Function)) {
    
                resolve(this.resolveData)
    
              } else {
    
                let data = onFulfilled(this.resolveData)
    
                this.resolvePromise(data, resolve, reject)
    
              }
    
            })
    
            this.onRejectedList.push(() => {
    
              //   then          
    
              if(!(onRejected instanceof Function)) {
    
                reject(this.rejectData)
    
              } else {
    
                let data = onRejected(this.rejectData)
    
                this.resolvePromise(data, resolve, reject)
    
              } 
    
            })
    
          })
    
        }
    
        // fulfilled    promise resolveData       then 
    
        //       ,              promise  
    
        else if (this.status === STATUS.FULFILLED) {
    
          promise = new Promise((resolve, reject) => {
    
            //   then          ,  resolve
    
            if(!(onFulfilled instanceof Function)) {
    
              resolve(this.resolveData)
    
            } else {
    
              let data = onFulfilled(this.resolveData)
    
              this.resolvePromise(data, resolve, reject)
    
            }      
    
          })
    
        }
    
        // rejected    fulfilled  
    
        else {
    
          promise = new Promise((resolve, reject) => {
    
            //   then          ,  reject
    
            if(!(onRejected instanceof Function)) {
    
              reject(this.rejectData)
    
            } else {
    
              let data = onRejected(this.rejectData)
    
              this.resolvePromise(data, resolve, reject)
    
            }        
    
          })
    
        }
    
        return promise
    
      }
    
      /**
    
      * catch  
    
      * @param {*reject  } rejectFn 
    
      */
    
      catch(rejectFn) {
    
        //        
    
        if(!(rejectFn instanceof Function)) {
    
          return
    
        }
    
        if(this.status === STATUS.PENDING) {  
    
          this.onRejectedList.push(() => {
    
            //           catch    
    
            if(this.rejectData !== null) {
    
              rejectFn(this.rejectData)
    
            } 
    
          })
    
        } else if (this.status = STATUS.REJECTED) {
    
          //           catch    
    
          if(this.rejectData !== null) {
    
            rejectFn(this.rejectData)
    
          }    
    
        }      
    
      }
    
      /**
    
      * resolve  ,
    
      * value promise         value resolveData    promise
    
      * @param {*} value 
    
      */
    
      static resolve(value) {
    
        if(value instanceof Promise) {
    
          return value
    
        }
    
        return new Promise((resolve, reject) => {    
    
          resolve(value)
    
        })
    
      }
    
      /**
    
      * reject  ,  resolve  
    
      * @param {*} value 
    
      */
    
      static reject(value) {
    
        if(value instanceof Promise) {
    
          return value
    
        }
    
        return new Promise((resolve, reject) => {    
    
          reject(value)
    
        })
    
      }
    
      /**
    
      * all  ,      promise
    
      *    promise  
    
      *                ,            reject      。
    
      * @param {*} promiseArray
    
      */
    
      static all(promiseArray) {
    
        if(!(promiseArray instanceof Array)) {
    
          throw new TypeError("parameter must be array")
    
        }
    
        let result = []
    
        let i = 0
    
        return new Promise((resolve, reject) => {
    
          if(promiseArray.length === 0) {
    
            resolve(result)
    
          } else {
    
            promiseArray.forEach((item, index) => {
    
              if(item instanceof Promise) {
    
                item.then(res => {
    
                  result[index] = res
    
                  i++
    
                  if(i === promiseArray.length) {
    
                    resolve(result)
    
                  }
    
                }, err => {
    
                  reject(err)
    
                })
    
              } 
    
              //        promise,              
    
              else {
    
                result[index] = item
    
                i++
    
                if(i === promiseArray.length) {
    
                  resolve(result)
    
                }
    
              }
    
            })
    
          }
    
        })  
    
      }
    
      /**
    
      * race  ,      promise
    
      *    promise  
    
      *         promise   ,  resolve  reject
    
      * @param {*} promiseArray 
    
      */
    
      static race(promiseArray) {
    
        if(!(promiseArray instanceof Array)) {
    
          throw new TypeError("parameter must be array")
    
        }
    
        //    ,   promise      true,    
    
        let flag = false
    
        return new Promise((resolve, reject) => {
    
          promiseArray.forEach((item) => {
    
            if(item instanceof Promise) {
    
              item.then(res => {
    
                if(!flag) {
    
                  flag = true
    
                  resolve(res)
    
                }
    
              }, err => {
    
                if(!flag) {
    
                  flag = true
    
                  reject(err)
    
                }          
    
              })
    
            } 
    
            //        promise,       
    
            else {
    
              if(!flag) {
    
                flag = true
    
                resolve(item)
    
              }
    
            }
    
          })
    
        })
    
      }
    
    }