美文网首页前端大杂烩
如何手写一个Promise

如何手写一个Promise

作者: 之幸甘木 | 来源:发表于2021-03-25 18:53 被阅读0次

    参考文章:《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

        本文链接:https://www.haomeiwen.com/subject/gtjrhltx.html