写点什么

假如面试官要你手写一个 promise

  • 2022 年 10 月 05 日
    浙江
  • 本文字数:4028 字

    阅读完需:约 13 分钟

promise

在开发中,经常需要用到 promise,promise 具有很多特性,这一次将对 promise 特性进行总结,并从零写一个 promise。

步骤一

  • Promise 特点

  • 1,创建时需要传递一个函数,否则会报错

  • 2,会给传入的函数设置两个回调函数

  • 3,刚创建的 Promise 对象状态是 pending


class MyPromise {  constructor(handle) {    // 3,刚创建的Promise对象状态是pending    this.status = "pending";    // 1,创建时需要传递一个函数,否则会报错    if (!this._isFunction(handle)) {      throw new Error("请传入一个函数");    }    // 2,会给传入的函数设置两个回调函数    handle(this._resolve.bind(this), this._reject.bind(this))  }  _resolve() {
} _reject() {
} _isFunction(fn) { return typeof fn === "function"; }}
复制代码

步骤二

  • Promise 特点

  • 4,状态一旦发生改变就不可再次改变

  • 5,可以通过 then 来监听状态的改变

  • 5.1,如果创建监听时,状态已经改变,立即执行监听回调

  • 5.2,如果创建监听时,状态未改变,会等状态改变后执行

  • 5.3,同一 promise 对象可以添加多个 then 监听,状态改变时按照注册顺序依次执行


// 定义常量保存对象的状态const PENDING = "pending";const FULFILLED = "fulfilled";const REJECTED = "rejected";
class MyPromise { constructor(handle) { // 3,刚创建的Promise对象状态是pending this.status = PENDING; // 成功回调的值 this.value = undefined; // 失败回调的值 this.reason = undefined; // 注册的成功回调 this.onResolvedCallbacks = []; // 注册的失败回调 this.onRejectedCallbacks = []; // 1,创建时需要传递一个函数,否则会报错 if (!this._isFunction(handle)) { throw new Error("请传入一个函数"); } // 2,会给传入的函数设置两个回调函数 handle(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { // 4,状态一旦发生改变就不可再次改变 if (this.status === PENDING) { this.status = FULFILLED; this.value = value; // 5.3,同一promise对象可以添加多个then监听,状态改变时按照注册顺序依次执行 this.onResolvedCallbacks.forEach(fn => fn(this.value)); } } _reject(reason) { // 4,状态一旦发生改变就不可再次改变 if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; // 5.3,同一promise对象可以添加多个then监听,状态改变时按照注册顺序依次执行 this.onRejectedCallbacks.forEach(fn => fn(this.reason)); } } then(onResolved, onRejected) { // 判断有没有传入成功的回调 if (this._isFunction(onResolved)) { // 5.1,如果创建监听时,状态已经改变,立即执行监听回调 if (this.status === FULFILLED) { onResolved(this.value); } } // 判断有没有传入失败的回调 if (this._isFunction(onRejected)) { // 5.1,如果创建监听时,状态已经改变,立即执行监听回调 if (this.status === REJECTED) { onRejected(this.reason); } } // 5.2,如果创建监听时,状态未改变,会等状态改变后执行 if (this.status === PENDING) { if (this._isFunction(onResolved)) { this.onResolvedCallbacks.push(onResolved); } if (this._isFunction(onRejected)) { this.onRejectedCallbacks.push(onRejected); } } } _isFunction(fn) { return typeof fn === "function"; }}
复制代码


参考 前端手写面试题详细解答

详解 then 方法

  • 接收两个参数:成功回调,失败回调

  • 如果 promise 失败了,但是没有注册失败监听,就会报错

  • then 方法每次执行完毕都会返回一个新的 Promise 对象

  • 如果 then 方法只有成功回调

  • 则它返回的 promise 的状态会继承当前 promise 的状态。

  • 如果当前 promise 的状态为成功:新 promise 的值为当前 then 的成功回调的返回值。

  • 如果当前 promise 的状态为失败:新的 promise 没有失败监听,则会报错

  • 如果 then 方法同时包含成功回调、失败回调

  • 则它返回的 promise 的状态都为成功,且值为成功或者失败回调的返回值。

  • 回调函数的返回值

  • 如果 then 方法的成功/失败回调返回的是 promise 对象

  • 则 then 方法返回的新的 promise 对象的状态由新 promise 的内部决定。

  • 且值为新 promise 的内 resolve/reject 函数传递的参数。

  • 如果 then 方法的成功/失败回调返回的是普通数据类型

  • 则 then 方法返回的新的 promise 对象的状态都为成功。

  • 且值为成功/失败回调的返回值,即都会传递给新的 promise 对象成功的回调。

  • 如果 then 方法的成功/失败回调没有返回值

  • 同返回普通数据类型

  • 失败回调函数

  • 可以捕获上一个 promise 对象的 then 方法中成功回调函数执行时的异常


then(onResolved, onRejected) {    return new MyPromise((nextResolve, nextReject) => {      // 1.判断有没有传入成功的回调      if (this._isFunction(onResolved)) {        // 2.判断当前的状态是否是成功状态        if (this.status === FULFILLED) {          try {            // 拿到上一个promise成功回调执行的结果            let result = onResolved(this.value);            // console.log("result", result);            // 判断执行的结果是否是一个promise对象            if (result instanceof MyPromise) {              result.then(nextResolve, nextReject);            } else {              // 将上一个promise成功回调执行的结果传递给下一个promise成功的回调              nextResolve(result);            }          } catch (e) {            nextReject(e);          }        }      }      // 1.判断有没有传入失败的回调      // if(this._isFunction(onRejected)){      try {        // 2.判断当前的状态是否是失败状态        if (this.status === REJECTED) {          let result = onRejected(this.reason);          if (result instanceof MyPromise) {            result.then(nextResolve, nextReject);          } else {            nextResolve(result);          }        }      } catch (e) {        nextReject(e);      }      // }      // 2.判断当前的状态是否是默认状态      if (this.status === PENDING) {        if (this._isFunction(onResolved)) {          // this.onResolvedCallback = onResolved;          this.onResolvedCallbacks.push(() => {            try {              let result = onResolved(this.value);              if (result instanceof MyPromise) {                result.then(nextResolve, nextReject);              } else {                nextResolve(result);              }            } catch (e) {              nextReject(e);            }          });        }        // if(this._isFunction(onRejected)){        // this.onRejectedCallback = onRejected;        this.onRejectedCallbacks.push(() => {          try {            let result = onRejected(this.reason);            if (result instanceof MyPromise) {              result.then(nextResolve, nextReject);            } else {              nextResolve(result);              nextReject();            }          } catch (e) {            nextReject(e);          }        });        // }      }    });}
复制代码

详解 catch 方法

  • 其实是 then 方法的失败回调函数的语法糖

  • 如果需要同时使用 then 和 catch 方法,必须使用链式编程,不然会报错

  • 可以捕获上一个 promise 对象的 then 方法中成功回调函数执行时的异常


catch(onRejected) {    return this.then(undefined, onRejected);}
复制代码

为啥使用 catch 时最好使用链式编程

  • 因为 then 方法只有成功回调,所以 p2 的状态会继承 p1

  • 又因为 p2 的状态为失败,且没有对 p2 进行失败监听,所以报错


let p1 = new Promise(function (resolve, reject) {  // resolve();  reject();});let p2 = p1.then(function () {  console.log("成功");});p1.catch(function () {  console.log("失败1");});
复制代码

Promise.all()

  • Promise.all(params)特点

  • 参数为一个数组,且数组元素为 promise 类型数据

  • 返回值为一个 promise,

  • 如果所有 promise 都执行成功

  • 返回值为所有 promise 都成功时返回的结果的集合

  • 如果有一个 promise 执行失败了,则返回失败的 promise


static all(list){    return new MyPromise(function (resolve, reject) {        let arr = [];        let count = 0;        for(let i = 0; i < list.length; i++){            let p = list[i];            p.then(function (value) {                // arr.push(value); 注意不要这样写,会导致结果顺序不对                arr[i] = value                count++;                if(list.length === count){                    resolve(arr);                }            }).catch(function (e) {                reject(e);            });        }    });}
复制代码

Promise.race()

  • Promise.race(params)特点

  • 参数为一个数组,且数组元素为 promise 类型数据

  • 返回值为一个 promise,且返回值为第一个成功或者失败的 promise 的值


static race(list){    return new MyPromise(function (resolve, reject) {        for(let p of list){            p.then(function (value) {                resolve(value);            }).catch(function (e) {                reject(e);            });        }    })}
复制代码


用户头像

还未添加个人签名 2022.07.31 加入

还未添加个人简介

评论

发布
暂无评论
假如面试官要你手写一个promise_JavaScript_helloworld1024fd_InfoQ写作社区