基于promise a+规范手写promise(promise其实没那么难!)

Posted on 2021-08-26 09:09  过鹿人  阅读(250)  评论(0)    收藏  举报

一、promise解决了哪些问题?

  1. 异步并发 待所有异步任务结束之后再执行我们的业务逻辑。
  2. 回调嵌套

二、promise特点

1. 每个promise都有三个状态,pending等待状态 fulfilled成功态 rejected失败态
2. 状态不可逆(若状态变成了成功态,则会一直维持这个状态)
3. 每个promise都有一个then方法 传入两个参数 一个是成功回调(参数是resolve执行时传的值) 一个是失败回调(参数是reject执行时传的值)
4. new Promise是同步执行的
5. 抛出异常的话 也会变成失败状态 走失败的回调
 

三、简单示例

    new Promise((resolve,reject) => {
      // 1.状态从pending改为fulfilled成功态,执行成功的回调
      resolve('success')
      // 2.状态从pending改为rejected失败态,执行失败的回调
      // reject('fail')
      // throw new Error('fail')
    }).then((data) => {
      console.log('success', data);
    }, (err) => {
      console.log('fail', err);
    })

四、手写实现

0.0.1版

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
  constructor(executor) {
    // 初始状态为pending等待状态
    this.status = PENDING;
    const resolve = (value) => {
      // 执行resolve状态变为成功态
      this.status = FULFILLED;
    };
    const reject = (reason) => {
      // 执行reject状态变为失败态
      this.status = REJECTED;
    };
    try {
      // 执行传入的回调函数
      executor(resolve, reject);
    } catch (e) {
      // 回调函数执行出错,也会执行reject
      reject(e);
    }
  }
}

由于promise的状态一旦转成了成功态或者失败态,就不能再改变状态了,所以我们需要加个判断,只有当状态为pending等待态的时候,才能将状态改为成功或失败,所以当状态变为了成功,再去调用resolve的话,无法满足判断条件,就不会继续执行了。

0.0.2版

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
  constructor(executor) {
    // 初始状态为pending等待状态
    this.status = PENDING;
    const resolve = (value) => {
      // 执行resolve状态变为成功态
      if (this.status === PENDING) {
        this.status = FULFILLED;
      }
    };
    const reject = (reason) => {
      // 执行reject状态变为失败态
      if (this.status === PENDING) {
        this.status = REJECTED;
      }
    };
    try {
      // 执行传入的回调函数
      executor(resolve, reject);
    } catch (e) {
      // 回调函数执行出错,也会执行reject
      reject(e);
    }
  }
}

这样调用的话,状态改变之后就不会再改了

    let p = new MyPromise((resolve,reject) => {
      resolve('success')
      reject('success')
    })
    console.log('p: ', p);

 

接下来我们实现一下promise实例的then方法,then方法的特点在上面有提到哦

0.0.3版

class MyPromise {
  constructor(executor) {
    // 赋值到this上是为了方便在then方法中调用
    // 初始状态为pending等待状态
    this.status = PENDING;
    // 成功回调的参数
    this.value = undefined;
    // 失败回调的参数
    this.reason = undefined;
    const resolve = (value) => {
      // 执行resolve状态变为成功态 保存用户传入的参数
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
      }
    };
    const reject = (reason) => {
      // 执行reject状态变为失败态 保存用户传入的参数
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
      }
    };
    try {
      // 执行传入的回调函数
      executor(resolve, reject);
    } catch (e) {
      // 回调函数执行出错,也会执行reject
      reject(e);
    }
  }
  // 接收两个函数作为参数,参数是用户传的,传的第一个回调就代表成功的回调,传的第二个回调就代表失败的回调
  then(onFulfilled, onRejected) {
    // 根据当前的状态,执行对应的回调。回调的参数为用户调用resolve或者reject传入的数据
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }
    if (this.status === REJECTED) {
      onRejected(this.reason);
    }
  }
}

调用时

    let p = new MyPromise((resolve,reject) => {
      resolve('success')
    })
    p.then((value) => {
      console.log(value);// success
    }, (reason) => {
      console.log(reason);
    })

 但是如果是在异步代码里面调用resolve方法,就不会执行成功回调了,例如:

    let p = new MyPromise((resolve,reject) => {
      setTimeout(() => {
        resolve('success')
      }, 300);
    })
    p.then((value) => {
      console.log(value);// success
    }, (reason) => {
      console.log(reason);
    })

这是因为代码执行到settimeout,会将回调放入宏任务队列,而不是立即执行,所以会先执行then方法,这个时候settimeout并未执行,所以resolve也未执行,此时的status = 'pending',then方法的两个回调都不会执行。

因此,我们想要执行异步代码里的resolve或者reject函数的话,需要在执行then方法的时候,先把成功回调或失败回调先存起来,直到异步代码执行到resolve,再依次执行回调。实现方式就是采用发布订阅模式,借助两个数组,一个是成功回调的数组,一个是失败回调的数组,在调用then时,若status = pending,就把回调存入对应的数组,之后在resolve(reject)函数中依次调用成功(失败)回调的数组的回调。

0.0.4版

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
  constructor(executor) {
    // 初始状态为pending等待状态
    this.status = PENDING;
    // 成功回调的参数
    this.value = undefined;
    // 失败回调的参数
    this.reason = undefined;
    this.onFulFilledCallbacks = []; // 存放成功的回调
    this.onRejectedCallbacks = []; // 存放失败的回调
    const resolve = (value) => {
      // 执行resolve状态变为成功态 保存用户传入的参数
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onFulFilledCallbacks.forEach((fn) => fn()); // 依次执行成功回调队列的回调
      }
    };
    const reject = (reason) => {
      // 执行reject状态变为失败态 保存用户传入的参数
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn()); // 依次执行失败回调队列的回调
      }
    };
    try {
      // 执行传入的回调函数
      executor(resolve, reject);
    } catch (e) {
      // 回调函数执行出错,也会执行reject
      reject(e);
    }
  }
  // 接收两个函数作为参数,参数是用户传的,传的第一个回调就代表成功的回调,传的第二个回调就代表失败的回调
  then(onFulfilled, onRejected) {
    // 根据当前的状态,执行对应的回调。回调的参数为用户调用resolve或者reject传入的数据
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }
    if (this.status === REJECTED) {
      onRejected(this.reason);
    }
    // 状态为pending时 将用户传的回调存放到各自的队列中(若用户没有调用resolve或reject,则不会执行队列中的回调)
    if (this.status === PENDING) {
      this.onFulFilledCallbacks.push(() => {
        onFulfilled(this.value);
      });
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason);
      });
    }
  }
}

调用时

    let p = new MyPromise((resolve,reject) => {
      setTimeout(() => {
        resolve('success')
      }, 300);
    })
    p.then((value) => {
      console.log(value, 1);// success 1
    }, (reason) => {
      console.log(reason);
    })
    p.then((value) => {
      console.log(value, 2);// success 2
    }, (reason) => {
      console.log(reason);
    })

接下来我们来分析then链式调用的规则

(1)如果then方法中成功回调或失败回调返回的是一个非promise值,则将这个值传递给外层下一次then的成功回调参数

(2)如果then方法中成功回调或失败回调的执行报错了,则将错误信息传递给外层下一次then的失败回调参数

    let pp = new Promise((resolve,reject) => {
      resolve(1)
    }).then((data) => {
      console.log('第一次 success', data);//第一次 success 1
      return 100;
      // throw new Error('error')
    }, (err) => {
      console.log('第一次 fail', err);
    }).then((data) => {
      console.log('第二次 success', data);//第二次 success 100
    }, (err) => {
      console.log('第二次 fail', err);
    })

(3)如果then方法中成功回调返回的是一个promise值,

  • 若在返回的这个promise内部调用了resolve函数,则将传入resolve的参数 传递给外层下一次then的成功回调参数;
  • 若在返回的这个promise内部调用了reject函数,则将传入reject的参数,传递给外层下一次then的失败回调参数;
    let pp2 = new Promise((resolve,reject) => {
      resolve(1)
    }).then((data) => {
      console.log('第一次 success', data);//第一次 success 1
      return new Promise((resolve, reject) => {
        resolve(100);
        // reject(200)
      });
    }, (err) => {
      console.log('第一次 fail', err);
    }).then((data) => {
      console.log('第二次 success', data);//第二次 success 100
    }, (err) => {
      console.log('第二次 fail', err);
    })

(4)catch方法相当于then(null, err => {})

    let pp2 = new Promise((resolve,reject) => {
      resolve(1)
    }).then((data) => {
      console.log('第一次 success', data);//第一次 success 1
      return new Promise((resolve, reject) => {
        // resolve(100);
        reject(200);
      });
    }, (err) => {
      console.log('第一次 fail', err);
    }).catch((data) => {
      console.log('第二次 fail', data);//第二次 fail 200
    });

then的链式调用是如何实现的?

每次调用then,返回一个新的promise实例,这个实例上肯定也有then方法,就可以一直.then下去

0.0.5版

class MyPromise {
  constructor(executor) {
    // 省略了跟上一版一样的内容
  }
  then(onFulfilled, onRejected) {
    // 调用then的时候 会创建一个新的promise实例并返回
    let promise2 = new MyPromise((resolve, reject) => {
      // 这里面的resolve和reject是promise2的 当在promise2里面调用resolve,就会执行promise2.then里面的成功回调
      if (this.status === FULFILLED) {
        // 需要拿到成功回调的返回值,传递给下一个then
        let x = onFulfilled(this.value);
        resolve(x);
      }
      if (this.status === REJECTED) {
        let x = onRejected(this.reason);
        resolve(x);
      }
      if (this.status === PENDING) {
        this.onFulFilledCallbacks.push(() => {
          let x = onFulfilled(this.value);
          resolve(x);
        });
        this.onRejectedCallbacks.push(() => {
          let x = onRejected(this.reason);
          resolve(x);
        });
      }
    });
    return promise2;
  }
}

这里涉及到MyPromise函数的递归执行,在调用函数时进行拆分,就好分析了

    let pp = new Promise((resolve,reject) => {
      // resolve(1)
      reject(1)
    })
    // 调用pp的then会返回promise2
    let promise2 = pp.then(data => {
      // 执行pp的成功回调 返回普通值x 会触发promise2的成功回调 相当于在这个promise2的内部调用resolve(x)
      // return 100;
    }, err => {
      // 执行pp的失败回调 也会触发promise2的成功回调
      return 100;
    })
    promise2.then((data) => {
      console.log(data);// 100
    }, (err) => {
      console.log(err);
    })

 上个版本只是实现了规则(1),处理的是成功或失败回调返回非promise值的情况,下面处理一下成功或失败的回调在执行时发生报错的情况,只需要加上try catch即可

0.0.6版

class MyPromise {
  constructor(executor) {
    // 此处省略
  }
  then(onFulfilled, onRejected) {
    // 调用then的时候 会创建一个新的promise实例并返回
    let promise2 = new MyPromise((resolve, reject) => {
      // 这里面的resolve和reject是promise2的 当在promise2里面调用resolve,就会执行promise2.then里面的成功回调
      if (this.status === FULFILLED) {
        // 需要拿到成功回调的返回值,传递给下一个then
        try {
          let x = onFulfilled(this.value);
          resolve(x);
        } catch (e) {
          reject(e);
        }
      }
      if (this.status === REJECTED) {
        try {
          let x = onRejected(this.reason);
          resolve(x);
        } catch (e) {
          reject(e);
        }
      }
      if (this.status === PENDING) {
        this.onFulFilledCallbacks.push(() => {
          try {
            let x = onFulfilled(this.value);
            resolve(x);
          } catch (e) {
            reject(e);
          }
        });
        this.onRejectedCallbacks.push(() => {
          try {
            let x = onRejected(this.reason);
            resolve(x);
          } catch (e) {
            reject(e);
          }
        });
      }
    });
    return promise2;
  }
}

接下来处理规则(3),也就是then方法中返回一个promise值的情况,需要给返回值注册成功和失败的回调,成功回调中执行promise2的resolve,失败回调中执行promise2的reject

规则(4)实际就是调用了then方法

0.0.7版(最终版)

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function resolvePromise(x, promise2, resolve, reject) {
  // then中成功或失败回调的返回值 不能和调用then方法返回的promise值相等
  if (x === promise2) {
    return reject(new TypeError('出错'));
  }
  if (x instanceof MyPromise) {
    try {
      let then = x.then; 
      then.call(
        x,
        (y) => {
          // y代表返回的promise值内部调用resolve时传的参数 传给promise2的resolve方法并执行
          resolve(y);
        },
        (r) => {
          // r代表返回的promise值内部调用reject时传的参数 传给promise2的reject方法并执行
          reject(r);
        },
      );
    } catch (e) {
      reject(e);
    }
  } else {
    // 非promise值
    resolve(x);
  }
}
class MyPromise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulFilledCallbacks = []; // 存放成功的回调
    this.onRejectedCallbacks = []; // 存放失败的回调
    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onFulFilledCallbacks.forEach((fn) => fn()); // 依次执行成功回调队列的回调
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn()); // 依次执行失败回调队列的回调
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // 使用settimeout是为了异步执行处理返回值的代码 否则直接使用promise2会报错
        setTimeout(() => {
          // 需要拿到成功回调的返回值,传递给下一个then
          try {
            let x = onFulfilled(this.value);
            // 统一处理返回值
            resolvePromise(x, promise2, resolve, reject);
          } catch (e) {
            console.log(e);
          }
        }, 0);
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(x, promise2, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }
      if (this.status === PENDING) {
        this.onFulFilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(x, promise2, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(x, promise2, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return promise2;
  }
    catch(onRejected) {
      return this.then(null, onRejected);
    }
}

 调用时

    let pp = new MyPromise((resolve,reject) => {
      resolve(1)
    })
    // 调用pp的then会返回promise2
    let promise2 = pp.then(data => {
      // 执行pp的成功回调 返回promise值x 会给x注册成功和失败回调,
      // 成功回调中执行promise2的成功回调,失败回调中执行promise2的失败回调
      return new MyPromise((res, rej) => {
        res(3000)
      })
    })
    // 若执行promise类型的返回值的resolve,就会执行第一个回调
    promise2.then((data) => {
      console.log(data);// 3000
    }, (err) => {
      console.log(err);
    })

 

 好啦!终于整理完成了!看完之后是不是发现promise其实也没那么难了!

博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3