手写一个promise

原生Promise

  • 首先,我们用原生的Promise实现一个简单的promise:
js 复制代码
const text = new Promise((resolve, reject) => {
  resolve("成功!!");
});

text.then(
  (res) => {
    console.log(res);
  },
  (err) => {}
);

运行上述代码,会进入promise的成功态resolve,输出"成功!!"

手写一个简单的Promise

接下来,我们自己手写一个Promise。

js 复制代码
// 定义promise的三个状态
const RESOLVE = "resolved";
const REJECT = "rejected";
const PENDING = "pending";

class ZZHPromise {
  status = PENDING;
  result = undefined;
  reason = undefined;
  constructor(excution) {
    // 成功态
    const resolve = (result) => {
      if (this.status === PENDING) {
        this.result = result;
        this.status = RESOLVE;
      }
    };
    // 失败态
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECT;
      }
    };
    excution(resolve, reject);
  }
  //   then方法里有两个参数,分别是两个方法
  then(onResolved, onRejected) {
    if (this.status === RESOLVE) {
      onResolved(this.result);
    }
    if (this.status === REJECT) {
      onRejected(this.reason);
    }
  }
}

module.exports = ZZHPromise;

首先定义了三个静态常量,为promise的三种状态。并且定义了resolve和reject两个函数,分别代表成功态和失败态。

为什么要判断状态为pending才会执行呢?这是因为,在promise中,一个流程中只能执行resolvereject,所以当执行完一种状态后,status被修改为当前状态,则promise就不会再继续执行另一个状态,才能达到promise的要求。

接下来来看then方法。then方法中会传入两个参数(onResolved, onRejected)为两个方法,分别代表成功态运行方法和失败态运行方法。在then方法中,是用status状态去判断现在应该执行哪个方法。

这样,我们就实现了一个简单的promise。在text文件中引入我们自己的promise,调用一下,输出"成功!!",说明这个简单的promise是没有问题的。

异步处理promise

我们看一下上图的代码,由于原生Promise是异步的,所以代码会优先执行同步任务console.log("我先执行");再执行Promise。但是如果用我们自己手写的Promise,就会先运行promise,再运行console.log("我先执行");

所以,需要让我们自己的promise也变成异步的。

其实很简单,我们只需要把onResolvedonRejected放在setTimeout中,让他们变成异步的。这样再来看一下输出结果,就会先输出"我先执行",再输出promise。

再来看一个新的问题,假如我们请求一个接口,在1s后才返回。这时,什么都没有输出。

这是因为,resolve会在1s后执行。而then并不会等待,而是优先执行,所以在then执行时,当前status还是pending。所以不会输出任何东西。

怎么解决呢?

我们可以用一个发布订阅模式,定义两个数组,onResolvedArronRejectedArr。当状态为pending时,将成功或失败的函数,推入数组中。并在方法中,循环这个数组。代码如下:

js 复制代码
// 定义promise的三个状态
const RESOLVE = "resolved";
const REJECT = "rejected";
const PENDING = "pending";

class ZZHPromise {
  status = PENDING;
  result = undefined;
  reason = undefined;
  onResolvedArr = [];
  onRejectedArr = [];
  constructor(excution) {
    // 成功态
    const resolve = (result) => {
      if (this.status === PENDING) {
        this.result = result;
        this.status = RESOLVE;
        this.onResolvedArr.map((fn) => fn());
      }
    };
    // 失败态
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECT;
        this.onRejectedArr.map((fn) => fn());
      }
    };
    excution(resolve, reject);
  }
  //   then方法里有两个参数,分别是两个方法
  then(onResolved, onRejected) {
    if (this.status === RESOLVE) {
      setTimeout(() => {
        onResolved(this.result);
      }, 0);
    }
    if (this.status === REJECT) {
      setTimeout(() => {
        onRejected(this.reason);
      }, 0);
    }
    if (this.status === PENDING) {
      this.onResolvedArr.push(() => onResolved(this.result));
      this.onRejectedArr.push(() => onRejected(this.reason)); 
    }
  }
}

module.exports = ZZHPromise;

这样,就会在1秒中之后,输出"成功!!"

链式调用Promise

我们都知道,promise是可以链式调用的。但是,我们自己的promise,由于现在是没有返回值的,返回值为undefined,所以是无法链式调用的。

所以我们应该返回一个promise:

js 复制代码
// 定义promise的三个状态
const RESOLVE = "resolved";
const REJECT = "rejected";
const PENDING = "pending";

class ZZHPromise {
  status = PENDING;
  result = undefined;
  reason = undefined;
  onResolvedArr = [];
  onRejectedArr = [];
  constructor(excution) {
    // 成功态
    const resolve = (result) => {
      if (this.status === PENDING) {
        this.result = result;
        this.status = RESOLVE;
        this.onResolvedArr.map((fn) => fn());
      }
    };
    // 失败态
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECT;
        this.onRejectedArr.map((fn) => fn());
      }
    };
    excution(resolve, reject);
  }
  //   then方法里有两个参数,分别是两个方法
  then(onResolved, onRejected) {
    const newPromise = new ZZHPromise((resolve, reject) => {
      if (this.status === RESOLVE) {
        setTimeout(() => {
          onResolved(this.result);
        }, 0);
      }
      if (this.status === REJECT) {
        setTimeout(() => {
          onRejected(this.reason);
        }, 0);
      }
      if (this.status === PENDING) {
        this.onResolvedArr.push(() => onResolved(this.result));
        this.onRejectedArr.push(() => onRejected(this.reason));
      }
    });
    return newPromise;
  }
}

module.exports = ZZHPromise;

这样,就可以进行链式调用,不过我们发现,then里面的res一直没有调用,这是因为resolve一直没有执行。所以,我们需要再定义一个方法去处理。并且做一下异常处理。

以下就是全部的promise代码:

js 复制代码
// 定义promise的三个状态
const RESOLVE = "resolved";
const REJECT = "rejected";
const PENDING = "pending";

const handlePromise = (result, newPromise, resolve, reject) => {
  if (result === newPromise) {
    throw new Error("can not return oneself");
  }
  //  判断result(promise的返回值)的类型
  if (
    (typeof result === "object" && typeof result !== null) ||
    typeof result === "function"
  ) {
    // 加一把锁,防止有的第三方promise会resolve, reject两个都执行。
    let lock = false;
    try {
      const then = result.then;
      // 因为有可能promise的then方法里返回的then还是一个promise,所以要当then不是一个promise时,再resolve出去.
      if (typeof then === "function") {
        then.call(
          result,
          (r) => {
            if (lock) return;
            handlePromise(r, newPromise, resolve, reject);
            lock = true;
          },
          (e) => {
            if (lock) return;
            reject(e);
            lock = true;
          }
        );
      } else {
        resolve(result);
      }
    } catch (error) {
      reject(error);
    }
  } else {
    resolve(result);
  }
};

class ZZHPromise {
  status = PENDING;
  result = undefined;
  reason = undefined;
  onResolvedArr = [];
  onRejectedArr = [];
  constructor(excution) {
    // 成功态
    const resolve = (result) => {
      if (this.status === PENDING) {
        this.result = result;
        this.status = RESOLVE;
        this.onResolvedArr.map((fn) => fn());
      }
    };
    // 失败态
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECT;
        this.onRejectedArr.map((fn) => fn());
      }
    };

    try {
      excution(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  //   then方法里有两个参数,分别是两个方法
  then(onResolved, onRejected) {
    onResolved = typeof onResolved === "function" ? onResolved : (data) => data;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (err) => {
            throw new Error(err);
          };

    const newPromise = new ZZHPromise((resolve, reject) => {
      if (this.status === RESOLVE) {
        setTimeout(() => {
          try {
            const result = onResolved(this.result);
            handlePromise(result, newPromise, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }
      if (this.status === REJECT) {
        setTimeout(() => {
          try {
            const result = onRejected(this.reason);
            handlePromise(result, newPromise, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }
      if (this.status === PENDING) {
        this.onResolvedArr.push(() => {
          try {
            const result = onResolved(this.result);
            handlePromise(result, newPromise, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
        this.onRejectedArr.push(() => {
          try {
            const result = onRejected(this.reason);
            handlePromise(result, newPromise, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }
    });
    return newPromise;
  }
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
}

module.exports = ZZHPromise;
相关推荐
flying robot几秒前
Rust的对web生态的影响
开发语言·前端·rust
艾斯特_5 分钟前
window.open 被浏览器拦截解决方案
前端·javascript
2401_8975796530 分钟前
软件架构的康威定律:AI如何重构团队协作模式
前端·人工智能·重构
小破孩呦32 分钟前
Vue3中使用 Vue Flow 流程图方法
前端·vue.js·流程图
Vec[95]1 小时前
将光源视角的深度贴图应用于摄像机视角的渲染
前端·人工智能·贴图
zhangfeng11331 小时前
要在Chrome和Firefox中获取LWP格式的cookie文件,可以通过以下步骤实现:
前端·chrome·firefox
zlting~1 小时前
【VUE】a链接下载跨域文件直接打开而非下载(解决办法)
前端·javascript·vue.js
叫兽~~1 小时前
uniapp 使用vue3写法,拿不到uni-popup的ref
前端·uni-app
nyf_unknown1 小时前
(vue)给循环遍历的el-select选择框加全选/清空/反选,禁选,添加行移除行功能
前端·javascript·vue.js
一颗不甘坠落的流星2 小时前
【React】刷新页面或跳转路由时进行二次确认
前端·javascript·react.js