手写一个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;
相关推荐
Cao_Ron4 分钟前
用 SVG image symbol 画 ECharts graph 圆形节点
前端
hxy06019 分钟前
React-Call让你的React组件像异步函数一样调用
前端·react.js
Hilaku10 分钟前
为什么 AI 写前端时,总是优先选择 React,而不是 Vue?
前端·javascript·程序员
京东云开发者12 分钟前
GPT-5.6、ChatGPT Work 与 Codex 更新食用指南
前端
Cao_Ron23 分钟前
用 ECharts graph 画出卡片式矩形节点
前端
skiyee31 分钟前
换个底座的 Wot Starter 居然让 AI 更懂项目!
前端·ai编程
anyup38 分钟前
这一套绝了!AI 大模型写故事,星云 SDK 驱动 3D 数字人实时演绎
前端·人工智能·aigc
耀耀切克闹灬1 小时前
短链跳转
前端
孟陬1 小时前
高质量全方位的 AI 工具 sse-stuntman — Mock SSE 接口
前端·node.js·openai
Cao_Ron1 小时前
给 ECharts 6 graph 节点叠加角标:从原理到性能优化
前端