从零开始手写模拟Promise

手写模拟Promise

璃安猫: 该文章中的MyPromise参考其他文章的Promise源码,然后使用自己的思路总结从零开始手写Promise的过程,若其中有不足之处劳烦指正。

分析Promise

Promise五种使用情况

  1. 链式执行同步任务
new Promise((resolve, reject) => {
  resolve('同步任务');
  // reject('同步 error');
}).then((value) => {
  console.log(value);
  return new Promise((resolve) => resolve(222))
}).catch((err) => {
  console.log(err);
})
  1. 链式执行异步任务并使用finally函数
new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('异步:ok');
  }, 1000);
}).then((value) => {
  console.log(value);
}).catch((err) => {
  console.log(err);
}).finally((value) => {
  console.log(value);
})
  1. 调用自身
// 第一种 验证then漏洞
const promise = new Promise(resolve => resolve(111))
  .then(() => {
    return promise
  })
promise.then(undefined, err => console.log(err));

// 第二种
const promise = Promise.resolve()
  .then(() => {
    return promise
  })
promise.catch(err=> console.log(err) );

  1. 使用resolve及reject快速创建Promise对象
Promise.resolve('111').then(v => console.log(v));
Promise.resolve(new Promise(resolve => resolve('222'))).then(v => console.log(v));

Promise.reject('111').catch(err => console.log(err));
Promise.reject(new Promise(resolve => resolve('222'))).catch(v => console.log(v));
  1. 使用all方法并行执行任务
const arr = ['111', Promise.resolve(222), Promise.resolve(333)];
Promise.all(arr).then(v => console.log(v)).catch(err => console.log(err));

const arr = ['111', Promise.resolve(222), Promise.reject('444'), Promise.resolve('111')];
Promise.all(arr).then(v => console.log(v)).catch(err => console.log(err));

Promsie结构分析

变量

变量名描述
status存储状态 :fulfilled rejected pending
value存储fulfilled状态结果
reason存储rejected状态结果
successCallback[]成功回调函数数组
failCallback[]失败回调函数数组

函数

实例方法描述
resolve改变自身状态并存储值
reject改变自身状态并存储值
then执行回调函数并实现链式调用
catch执行失败回调函数并实现链式调用
finally不论失败或成功都执行回调(不获取结果值,仅传递结果)
静态方法描述
resolve快速创建成功的Promise
reject快速创建失败的Promise
all实现并行执行Promise任务

基础结构代码

// 三个状态值为全局变量  
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  status = PENDING;
  value;
  reason;
  successCallback = [];
  failCallback = [];

  // 构造函数
  constructor (Fn) {}

  // 由于该函数调用在传参函数内部 即  resolve, 若为普通函数this则指向全局
  // 因此使用箭头函数将this指向MyPromise对象
  resolve = value => {}

  // 此处同理
  reject = reason => {}

  // then及以下的函数使用方式都为  .then , 因此this指向MyPromise 不需要使用箭头函数
  then (successCallback, failCallback) {}

  catch (failCallback) {}

  finally () {}

  static resolve () {}

  static reject () {}

  static all () {}
}

完善constructor & 实现 resolve reject

class MyPromise {
  status = PENDING;
  value;
  reason;
  successCallback = [];
  failCallback = [];

  // 构造函数接收传参
  constructor (Fn) {
    try {
      // 执行传入函数
      Fn(this.resolve, this.reject);
    } catch (err) {
      this.reject(err);
    } 
  }

  // 由于该函数调用在传参函数内部 即  resolve, 若为普通函数this则指向全局
  // 因此使用箭头函数将this指向MyPromise对象
  resolve = value => {
    // 状态一经确认不可更改
    if(this.status !== PENDING) return;
    this.status = FULFILLED;
    this.value = value;
    // 若有回调函数则逐个执行回调函数
    // 注: 此处回调是执行异步任务是status仍未改变 但then已执行,因此会在then中存入回调任务
    while(this.successCallback.length) {
      // 将任务从头出栈并执行
      this.successCallback.shift()();
    }
  }

  // 此处同理
  reject = reason => {
    if(this.status !== PENDING) return;
    this.status = REJECTED;
    this.reason = reason;
    while(this.failCallback.length) {
      this.failCallback.shift()();
    }
  }
}

实现then

class MyPromise {
  // ...

  // then及以下的函数使用方式都为  .then , 因此this指向MyPromise 不需要使用箭头函数
  then(successCallback, failCallback) {
    // 如果没有传入回调函数,则将值继续传递
    successCallback = successCallback ? successCallback : value => value;
    failCallback = failCallback ? failCallback : reason => { throw reason };

    // 返回promise用于then链式调用
    const promise = new MyPromise((resolve, reject) => {
      // 判断 .then前的Promise的状态  此处this指向.then前的promise
      switch (this.status) {
        case FULFILLED: {
          // 使用异步任务以延迟执行该代码从而获取当前的promise
          setTimeout(() => {
            try {
              const result = successCallback(this.value);
              // 该函数用于判断返回结果是否为自身以及promise等情况
              resolvePromise(promise, result, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
          break;
        };
        case REJECTED: {
          setTimeout(() => {
            try {
              const result = failCallback(this.reason);
              resolvePromise(promise, result, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
          break;
        };
        // 该情况是promsie中为异步任务,因此执行then时status还未改变
        // 因此将回调函数存储起来
        case PENDING: {
          // 存入成功回调
          this.successCallback.push(() => {
            setTimeout(() => {
              try {
                const result = successCallback(this.value);
                // 该函数用于判断返回结果是否为自身以及promise等情况
                resolvePromise(promise, result, resolve, reject);
              } catch (err) {
                reject(err);
              }
            }, 0);
          });

          // 存入失败回调
          this.failCallback.push(() => {
            setTimeout(() => {
              try {
                const result = failCallback(this.reason);
                resolvePromise(promise, result, resolve, reject);
              } catch (err) {
                reject(err);
              }
            }, 0);
          });
        }
      }
    });

    return promise;
  }

  //...
}

// 处理then返回的promise的结果状态
function resolvePromise(promise, result, resolve, reject) {
  // 返回的结果为自身promise 造成循环调用
  if (promise === result) {
    reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
  }
  // 返回结果为promise
  if (result instanceof MyPromise) {
    result.then(v => resolve(v));
  }
  // 返回结果为普通值
  else {
    resolve(result);
  }
}

至此,可以使用自己手写的Promise验证开头的 1-3 种使用情况 (除去catch及finally)
验证完毕无误后,接着实现其他方法

实现catch 及 finally

class MyPromsie {
  // ....

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

  finally(callback) {
    // 执行回调函数,并将上一个promise返回的value继续传递
    return this.then(value => {
      // 若callback为异步任务则需等待异步任务完成再执行下一个任务
      // 因此使用 Promise包装callback 避免不等待callback直接执行下一个任务
      // 此外不使用finally回调函数的返回值,只传递上一个任务的值
      return new MyPromise(resolve => resolve(callback())).then(() => value);
    }, reason => {
      return new MyPromise(resolve => resolve(callback())).then(() => { throw reason });
    })
  }

  // ...
}

至此,可以使用自己手写的Promise验证开头的 1-3 种使用情况
验证完毕无误后,接着实现其他方法

实现静态方法resolve及reject,优化finally

class MyPromise {
  // ...

  finally(callback) {
    // 执行回调函数,并将上一个promise返回的value继续传递
    return this.then(value => {
      // 若callback为异步任务则需等待异步任务完成再执行下一个任务
      // 因此使用 Promise包装callback 避免不等待callback直接执行下一个任务
      // 此外不使用finally回调函数的返回值,只传递上一个任务的值
      return MyPromise.resolve(callback()).then(() => value);
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason });
    })
  }

  static resolve(value) {
    // 如果value是promise则直接返回
    if (value instanceof MyPromise) return value;
    // 如果value是普通值则创建返回
    else {
      return new MyPromise((resolve) => resolve(value));
    }
  }

  static reject(reason) {
    // 如果value是promise则直接返回
    if (reason instanceof MyPromise) return reason;
    // 如果value是普通值则创建返回
    else {
      return new MyPromise((resolve, reject) => reject(reason));
    }
  }

  // ...
}

至此,可以使用自己手写的Promise验证开头的 1-4 种使用情况
验证完毕无误后,接着实现其他方法

实现all方法

class MyPromise {
  // ...

  static all(arr) { 
    // 返回一个promise
    return new MyPromise((resolve, reject) => {
      // 用于记录返回结果
      const result = [];
      function addData (i, value) {
        result[i] = value;
        //判断结果数量是否对应 用于等待异步处理的结果
        if (result.length === arr.length) {
          resolve(result);
        }
      }

      arr.forEach((value, i) => {
        // 如果value为promise
        if (value instanceof MyPromise) {
          value.then(v => addData(i, v), err => reject(err));
        }
        // 如果value为普通值
        else {
          addData(i, value);
        }
      })
    })
  }
}

至此,可以使用自己手写的Promise验证开头的 1-5 种使用情况
验证完毕无误后,Promise功能基本实现

完整的MyPromise代码

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  status = PENDING;
  value;
  reason;
  successCallback = [];
  failCallback = [];

  // 构造函数接收传参
  constructor(Fn) {
    try {
      // 执行传入函数
      Fn(this.resolve, this.reject);
    } catch (err) {
      this.reject(err);
    }
  }

  // 由于该函数调用在传参函数内部 即  resolve, 若为普通函数this则指向全局
  // 因此使用箭头函数将this指向MyPromise对象
  resolve = value => {
    // 状态一经确认不可更改
    if (this.status !== PENDING) return;
    this.status = FULFILLED;
    this.value = value;
    // 若有回调函数则逐个执行回调函数
    // 注: 此处回调是执行异步任务是status仍未改变 但then已执行,因此会在then中存入回调任务
    while (this.successCallback.length) {
      // 将任务从头出栈并执行
      this.successCallback.shift()();
    }
  }

  // 此处同理
  reject = reason => {
    if (this.status !== PENDING) return;
    this.status = REJECTED;
    this.reason = reason;
    while (this.failCallback.length) {
      this.failCallback.shift()();
    }
  }

  // then及以下的函数使用方式都为  .then , 因此this指向MyPromise 不需要使用箭头函数
  then(successCallback, failCallback) {
    // 如果没有传入回调函数,则将值继续传递
    successCallback = successCallback ? successCallback : value => value;
    failCallback = failCallback ? failCallback : reason => { throw reason };

    // 返回promise用于then链式调用
    const promise = new MyPromise((resolve, reject) => {
      // 判断 .then前的Promise的状态  此处this指向.then前的promise
      switch (this.status) {
        case FULFILLED: {
          // 使用异步任务以延迟执行该代码从而获取当前的promise
          setTimeout(() => {
            try {
              const result = successCallback(this.value);
              // 该函数用于判断返回结果是否为自身以及promise等情况
              resolvePromise(promise, result, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
          break;
        };
        case REJECTED: {
          setTimeout(() => {
            try {
              const result = failCallback(this.reason);
              resolvePromise(promise, result, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
          break;
        };
        // 该情况是promsie中为异步任务,因此执行then时status还未改变
        // 因此将回调函数存储起来
        case PENDING: {
          // 存入成功回调
          this.successCallback.push(() => {
            setTimeout(() => {
              try {
                const result = successCallback(this.value);
                // 该函数用于判断返回结果是否为自身以及promise等情况
                resolvePromise(promise, result, resolve, reject);
              } catch (err) {
                reject(err);
              }
            }, 0);
          });

          // 存入失败回调
          this.failCallback.push(() => {
            setTimeout(() => {
              try {
                const result = failCallback(this.reason);
                resolvePromise(promise, result, resolve, reject);
              } catch (err) {
                reject(err);
              }
            }, 0);
          });
        }
      }
    });

    return promise;
  }

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

  finally(callback) {
    // 执行回调函数,并将上一个promise返回的value继续传递
    return this.then(value => {
      // 若callback为异步任务则需等待异步任务完成再执行下一个任务
      // 因此使用 Promise包装callback 避免不等待callback直接执行下一个任务
      // 此外不使用finally回调函数的返回值,只传递上一个任务的值
      return MyPromise.resolve(callback()).then(() => value);
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason });
    })
  }

  static resolve(value) {
    // 如果value是promise则直接返回
    if (value instanceof MyPromise) return value;
    // 如果value是普通值则创建返回
    else {
      return new MyPromise((resolve) => resolve(value));
    }
  }

  static reject(reason) {
    // 如果value是promise则直接返回
    if (reason instanceof MyPromise) return reason;
    // 如果value是普通值则创建返回
    else {
      return new MyPromise((resolve, reject) => reject(reason));
    }
  }

  static all(arr) { 
    // 返回一个promise
    return new MyPromise((resolve, reject) => {
      // 用于记录返回结果
      const result = [];
      function addData (i, value) {
        result[i] = value;
        // 判断结果数量是否对应 用于等待异步处理的结果
        if (result.length === arr.length) {
          resolve(result);
        }
      }

      arr.forEach((value, i) => {
        // 如果value为promise
        if (value instanceof MyPromise) {
          value.then(v => addData(i, v), err => reject(err));
        }
        // 如果value为普通值
        else {
          addData(i, value);
        }
      })
    })
  }
}

// 处理then返回的promise的结果状态
function resolvePromise(promise, result, resolve, reject) {
  // 返回的结果为自身promise 造成循环调用
  if (promise === result) {
    reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
  }
  // 返回结果为promise
  if (result instanceof MyPromise) {
    result.then(v => resolve(v));
  }
  // 返回结果为普通值
  else {
    resolve(result);
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值