如何手写一个Promise

参考文章:《Promise,从入门到放弃》

为了方便比较与原装的Promise区别,手写的Promise被命名为iPromise

实现本体

  1. 首先,Promise是一个类,接收一个函数executor作为构造函数,该函数接受两个函数作为参数:resolverejectPromise自带,并不需要使用者手动部署),且立即(同步)执行。

Promise对象的promiseResult属性存储执行的结果,promiseState属性存储状态。
Promise有三种状态:pending(准备中)、fulfilled(满足)和rejected(拒绝)。初始状态是pending

class iPromise {
  constructor(executor) {
    // 存储promise结果
    this.promiseResult = undefined;
    // 存储promise的状态
    this.promiseState = 'pending';
    
    // 立即执行executor
    executor(resolve, reject);
  }

}
  1. resolve方法的作用是将执行所得的结果赋值给promiseResult,并将promiseStatepending变为fulfilledreject方法基本一样,只是将promiseStatepending变为rejected

promise对象的状态变化只能变化一次;
executor执行出现错误,也会触发reject函数。

class iPromise {
  constructor(executor) {
    // 存储promise结果
    this.promiseResult = undefined;
    // 存储promise的状态
    this.promiseState = 'pending';
    
    // resolve方法,将promiseState变为fulfilled,并修改promiseResult
    const resolve = (value) => {
      // 仅在promiseState为pending的时候变化
      if (this.promiseState !== 'pending') return;
      // 将promiseState变为fulfilled
      this.promiseState = 'fulfilled';
      // 将value作为promiseResult
      this.promiseResult = value;
    }
    
    // reject方法,将promiseState变为rejected,并修改promiseResult
    const reject = (error) => {
      // 仅在promiseState为pending的时候变化
      if (this.promiseState !== 'pending') return;
      // 将promiseState变为rejected
      this.promiseState = 'rejected';
      // 将error作为promiseResult
      this.promiseResult = error;
    }
    
    // 立即执行executor
    // executor函数执行出现错误,会调用reject方法
    try {
      executor(resolve, reject);
    } catch (error) {
        reject(error);
    }
  }

}

实现then方法

  1. Promise.then()方法接收1~2个回调函数作为参数,返回一个新的Promise对象(下文中如果没有特殊说明,Promise对象都指原Promise对象)以支持链式调用

返回的新Promise对象的参数必须使用箭头函数()=>{},否则会造成this指向错误的问题。当然,你也可以采取let self = this;存储this然后传入的形式,不过有点多此一举。

class iPromise {
  constructor(executor){
    // 构造函数
  }
  
  // 接收两个回调函数作为参数
  then(onResolved, onRejected) {
    /*
    * 这里必须要写箭头函数,否则this会指向新的Promise对象
    * 进而导致取不到promiseState和promiseResult
    */ 
    return new iPromise((resolve, reject) => {
      // ...
    })
  }
}
  1. 根据promiseResult的值不同,分为两种情况:当其为Promise对象时,递归执行它的then方法;当其为其他类型,直接调用resolve方法。
class iPromise {
  constructor(executor){
    // ...
  }
  
  // 接收两个回调函数作为参数
  then(onResolved, onRejected) {
    /*
    * 这里必须要写箭头函数,否则this会指向新的Promise对象
    * 进而导致取不到promiseState和promiseResult
    */ 
    return new iPromise((resolve, reject) => {
      
      /*
      * 回调处理函数
      * 这里也请记得用箭头函数,this要穿透几层
      * 箭头函数就用几层
      */
      const handleCallback = (callback) => {
        try {
            let res = callback(this.promiseResult);
            // 若返回值是promise对象
            if (res instanceof Promise) {
              res.then(val => resolve(val), err => reject(err));
            } else {
              // 若不是
              resolve(res);
            }
        } catch (error) {
          reject(error);
        }
      }
    })
  }
}
  1. 根据promiseState的值来确定应该执行哪个回调函数:
class iPromise {
  constructor(executor){
    // ...
  }
  
  // 接收两个回调函数作为参数
  then(onResolved, onRejected) {
    /*
    * 这里必须要写箭头函数,否则this会指向新的Promise对象
    * 进而导致取不到promiseState和promiseResult
    */ 
    return new iPromise((resolve, reject) => {
      
      /*
      * 回调处理函数
      * 这里也请记得用箭头函数,this要穿透几层
      * 箭头函数就用几层
      */
      const handleCallback = (callback) => {
        try {
            let res = callback(this.promiseResult);
            // 若返回值是promise对象
            if (res instanceof Promise) {
              res.then(val => resolve(val), err => reject(err));
            } else {
              // 若不是
              resolve(res);
            }
        } catch (error) {
          reject(error);
        }
      }
      
      // promiseState为fulfilled时调用onResolved
      if (this.promiseState === "fulfilled") {
        handleCallback(onResolved);
      }
      
      // promiseState为rejected时调用onRejected
      if (this.promiseState === "rejected") {
        handleCallback(onRejected);
      }
    })
  }
}
  1. 因为异步任务的问题,并且支持多个回调,所以我们需要对回调函数采用数组进行存储,所以引入了新的变量:callbackList
class iPromise {
  constructor(executor) {
    // 存储promise结果
    this.promiseResult = undefined;
    // 存储promise的状态
    this.promiseState = 'pending';
    // 存储所有的回调函数
    this.callbackList = [];
    
    // resolve方法,将promiseState变为fulfilled,并修改promiseResult
    const resolve = (value) => {
      // 仅在promiseState为pending的时候变化
      if (this.promiseState !== 'pending') return;
      // 将promiseState变为fulfilled
      this.promiseState = 'fulfilled';
      // 将value作为promiseResult
      this.promiseResult = value;
      // 异步执行所有回调函数
      this.callbackList.forEach(cb => cb.onResolved(value));
    }
    
    // reject方法,将promiseState变为rejected,并修改promiseResult
    const reject = (error) => {
      // 仅在promiseState为pending的时候变化
      if (this.promiseState !== 'pending') return;
      // 将promiseState变为rejected
      this.promiseState = 'rejected';
      // 将error作为promiseResult
      this.promiseResult = error;
      // 异步执行所有回调函数
      this.callbackList.forEach(cb => cb.onRejected(error));
    }
    
    // 立即执行executor
    // executor函数执行出现错误,会调用reject方法
    try {
      executor(resolve, reject);
    } catch (error) {
        reject(error);
    }
  }
  
  // 接收两个回调函数作为参数
  then(onResolved, onRejected) {
    /*
    * 这里必须要写箭头函数,否则this会指向新的Promise对象
    * 进而导致取不到promiseState和promiseResult
    */ 
    return new iPromise((resolve, reject) => {
      
      /*
      * 回调处理函数
      * 这里也请记得用箭头函数,this要穿透几层
      * 箭头函数就用几层
      */
      const handleCallback = (callback) => {
        try {
            let res = callback(this.promiseResult);
            // 若返回值是promise对象
            if (res instanceof Promise) {
              res.then(val => resolve(val), err => reject(err));
            } else {
              // 若不是
              resolve(res);
            }
        } catch (error) {
          reject(error);
        }
      }
      
      // promiseState为fulfilled时调用onResolved
      if (this.promiseState === "fulfilled") {
        handleCallback(onResolved);
      }
      
      // promiseState为rejected时调用onRejected
      if (this.promiseState === "rejected") {
        handleCallback(onRejected);
      }
      
      /*
       * 如果是pending状态,则异步任务,在改变状态的时候去调用回调函数
       * 所以要保存回调函数
       * 因为promise实例可以指定多个回调,于是采用数组 
       */
      if (this.promiseState === "pending") {
        this.callbackList.push({
            onResolved: () => {
              handleCallback(onResolved)
            },
            onRejected: () => {
              handleCallback(onRejected)
            }
        })
      }
    })
  }
}

catch方法

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

当使用promisethen进行链式调用时,可以在最后指定失败的回调。前面的任何错误都会在最后传到失败的回调中去处理,除非在中途被失败回调函数(onRejected)处理了。

// promise对象的异常穿透
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

要实现catch,我们可以直接调用iPromise.then方法,但不传入onResolve方法。

同时,我们需要给then中的onResolveonRejected赋初始值,顺便避免了传入undefined或其他非函数值而报错:

class iPromise {
  constructor(executor) {
    // 构造函数
  }
  
  // 接收两个回调函数作为参数
  then(onResolved, onRejected) {
  
    // 处理异常穿透,并设置默认值以避免程序出错
    if(typeof onResolved !== 'function') {
      onResolve = (val) => val;
    }
    
    if(typeof onRejected !== 'function') {
      onRejected = (err) => {
        throw err;
      }
    }
    
    return new iPromise((resolve, reject) => {
      // ...调用回调函数
    })
  }
  
  // catch方法
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
}

Promise.resolve方法

Promise.resolve方法返回成功或者失败的Promise对象。如果传入的参数为非Promise类型的对象,则返回的结果为成功的Promise对象。如果传入的参数为Promise对象,则参数Promise返回的结果就是 Promise.resolve返回的结果。

let promiseA = Promise.resolve(1);

// 比如这时return 一个[[PromiseResult]]的值为err的Promise对象。
let PromiseB = Promise.resolve(new Promise((resolve,reject)=>{
    reject('err');
})

实现它,我们需要用到静态方法:

class iPromise {
  constructor(executor) {
    // 构造函数
  }
  
  // ...其他方法
  
  // 静态方法只能通过类本身来调用
  static resolve(value) {
    return new iPromise((resolve, reject) => {
      // 如果是iPromise对象
      if (value instanceof iPromise) {
        value.then(val => resolve(val), err => reject(err));
      } else {
        resolve(value);
      }
    })
  }
}

Promise.reject方法

Promise.reject方法返回一个失败的Promise对象,promiseResult的值为Promise.reject参数

有多失败?大概像我一样失败。

let PromiseA = Promise.reject(new Promise((resolve,reject)=>{
    resolve('err');
})
// 无论传入是啥,就返回一个失败的Promise对象,[[PromiseResult]]的值为 Promise.reject的参数

实现:

class iPromise {
  constructor(executor) {
    // 构造函数
  }
  
  // ...其他方法
  
  // 静态方法只能通过类本身来调用
  static reject(error) {
    return new iPromise((resolve, reject) => {
      reject(error);
    })
  }
}

Promise.all方法

Promise.all方法接收的参数是由n个Promise对象的数组。返回新的Promise对象,只有所有的Promise对象都成功才成功,返回的对象的promiseResult为包含所有Promise对象的数组。只要有一个失败了就直接失败,返回的对象的promiseResult为失败的Promise对象的执行结果。

class iPromise {
  constructor(executor) {
    // 构造函数
  }
  
  // ...其他方法
  
  static all(promiseArrays) {
    return new iPromise((resolve, reject) => {
      // 用以存储执行的结果
      let results = [];
      let length  = promiseArrays.length;
      promiseArrays.forEach((promiseObj, index, promiseArrays) => {
        promiseObj.then((val) => {
          results.push(val);
          // 由于是多个异步任务的关系,需要判断是否都执行完毕
          if (results.length === length) {
            resolve(results);
          }
        }, err => {
          // 如有错误,则reject
          reject(err);
        });
      })
    })
  }
  
}

Promise.race方法

Promise.race方法接收的参数是由n个Promise对象的数组。返回新的Promise对象,第一个完成的Promise的结果状态就是最终结果的状态。

你可能会问:那不铁定第一个Promise对象是第一个完成的吗?
这是因为我们的最重要的功能还没做:异步。

class iPromise {
  constructor(executor) {
    // 构造函数
  }
  
  // ...其他方法
  
  // race方法
  static race(promiseArrays) {
    return new iPromise((resolve, reject) => {
      promiseArrays.forEach(promiseObj => {
        promiseObj.then(val => {
          resolve(val);
        }, err => {
          reject(err);
        });
      })
    })
  }
  
}

加点细节—由同步到异步

使用setTimeout将其变为异步任务。

setTimeout只能将任务变更为宏观异步任务。原装的Promise是微观异步任务。

class iPromise {

  constructor(executor) {
    // 存储promise结果
    this.promiseResult = undefined;
    // 存储promise的状态
    this.promiseState = 'pending';
    // 存储所有的回调函数
    this.callbackList = [];
    
    // resolve方法,将promiseState变为fulfilled,并修改promiseResult
    const resolve = (value) => {
      // 仅在promiseState为pending的时候变化
      if (this.promiseState !== 'pending') return;
      // 将promiseState变为fulfilled
      this.promiseState = 'fulfilled';
      // 将value作为promiseResult
      this.promiseResult = value;
      // 异步执行所有回调函数
      setTimeout(()=>{
        this.callbackList.forEach(cb => cb.onResolved(value));
      })
    }
    
    // reject方法,将promiseState变为rejected,并修改promiseResult
    const reject = (error) => {
      // 仅在promiseState为pending的时候变化
      if (this.promiseState !== 'pending') return;
      // 将promiseState变为rejected
      this.promiseState = 'rejected';
      // 将error作为promiseResult
      this.promiseResult = error;
      // 异步执行所有回调函数
      setTimeout(()=>{
        this.callbackList.forEach(cb => cb.onRejected(error));
      })
    }
    
    // 立即执行executor
    // executor函数执行出现错误,会调用reject方法
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  // 接收两个回调函数作为参数
  then(onResolved, onRejected) {
    //处理异常穿透并且为onResolved,onRejected设置默认值。因为这两个参数可以都不传
    if (typeof onRejected !== 'function') {
      onRejected = err => {
        throw err;
      }
    }
    if (typeof onResolved !== 'function') {
      onResolved = val => val;
    }
    /*
    * 这里必须要写箭头函数,否则this会指向新的Promise对象
    * 进而导致取不到promiseState和promiseResult
    */
    return new iPromise((resolve, reject) => {
      /*
      * 回调处理函数
      * 这里也请记得用箭头函数,this要穿透几层
      * 箭头函数就用几层
      */
      const handleCallback = (callback) => {
        try {
          let res = callback(this.promiseResult);
          // 若返回值是promise对象
          if (res instanceof iPromise) {
            res.then(val => resolve(val), err => reject(err));
          } else {
            // 若不是
            resolve(res);
          }
        } catch (error) {
          reject(error);
        }
      }
      // promiseState为fulfilled时调用onResolved
      if (this.promiseState === "fulfilled") {
        setTimeout(() => {
          handleCallback(onResolved);
        });
      }
      // promiseState为rejected时调用onRejected
      if (this.promiseState === "rejected") {
        setTimeout(() => {
          handleCallback(onRejected);
        });
      }
      /*
      * 如果是pending状态,则异步任务,在改变状态的时候去调用回调函数
      * 所以要保存回调函数
      * 因为promise实例可以指定多个回调,于是采用数组 
      */
      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) => {
      // 用以存储执行的结果
      let results = [];
      let length  = promiseArrays.length;
      promiseArrays.forEach((promiseObj, index, promiseArrays) => {
        promiseObj.then((val) => {
          results.push(val);
          // 由于是多个异步任务的关系,需要判断是否都执行完毕
          if (results.length === length) {
            resolve(results);
          }
        }, err => {
          // 如有错误,则reject
          reject(err);
        });
      })
    })
  }

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

总结

......没啥好说的,从上到下敲一遍,不说运用自如,怎么也得是一脸懵逼吧。

你可能感兴趣的:(如何手写一个Promise)