手写Promise-手把手教你从零实现一个自己的Promise

1-什么是 Promise ?

在 JavaScript 中,Promise 是一种用于处理异步操作的对象,它可以更加优雅地处理回调函数嵌套和错误处理。

解决的问题:

// 1)回调写起来不好看(难以维护) 恶魔金字塔 嵌套逻辑不优雅 (链式调用 then)
// 2)错误处理无法统一  我们需要处理公共的错误逻辑 (catch)
// 3)尽量简化回调  多个异步并发问题 (Promise.all Promise.finllay)


// promise 依旧是基于回调的,可能还是会有嵌套问题

promise es6已经内部实现了, ie 不兼容 promise,需要 polyfill (比如:es-promise)

2-实现一个简单的promise

// 1) promise 是一个构造函数,默认需要传入一个executor执行器
// 2) executor 会立刻执行,并且传入 resolve 和 reject 两个参数
// 3) promise 有三个状态 fulfilled 成功  reject 拒绝态   pending等待态 (默认是等待态)
// 4)每个promise都有一个then方法 , 可以访问到成功的值和失败的原因
// 5) 可以通过resolve和reject来改变状态,同时调用对应的回调, 一个promise实例状态变化后,不能再重新的发生变化
// 6) 或者当executor 发生异常的时候 也会触发promise的失败

代码实现:

const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
   
   
  constructor(executor) {
   
   
    this.state = PENDING; // new Promise 后 默认状态为pending
    this.value = undefined; //成功的原因
    this.reason = undefined; //失败的原因

    const resolve = (value) => {
   
   
      //只有状态是pending的时候 才可以修改状态
      if (this.state === PENDING) {
   
   
        this.state = FULFILLED;
        this.value = value;
      }
    };

    const reject = (reason) => {
   
   
      if (this.state === PENDING) {
   
   
        this.state = REJECTED;
        this.reason = reason;
      }
    };
    // 调用executor 会自动传入resolve 与 reject
    try {
   
   
      //或者当executor 发生异常的时候 也会触发promise的失败
      executor(resolve, reject);
    } catch (error) {
   
   
      reject(error);
    }
  }
  then(onFulfilled, onRejected) {
   
   
    if (this.state === FULFILLED) {
   
   
      onFulfilled(this.value);
    }

    if (this.state === REJECTED) {
   
   
      onRejected(this.reason);
    }
  }
}

module.exports = Promise;

//common.js 规范

使用自己的promise

// promise 干什么的,解决的问题是什么?

// 1)回调写起来不好看(难以维护) 恶魔金字塔 嵌套逻辑不优雅 (链式调用 then)
// 2)错误处理无法统一  我们需要处理公共的错误逻辑 (catch)
// 3)尽量简化回调  多个异步并发问题 (Promise.all Promise.finllay)

// promise 依旧是基于回调的,可能还是会有嵌套问题

// 1) promise 是一个构造函数,默认需要传入一个executor执行器
// 2) executor 会立刻执行,并且传入 resolve 和 reject 两个参数
// 3) promise 有三个状态 fulfilled 成功  rejected 拒绝态   pending等待态 (默认是等待态)
// 4)每个promise都有一个then方法 , 可以访问到成功的值和失败的原因
// 5) 可以通过resolve和reject来改变状态,同时调用对应的回调,
//    一个promise实例状态变化后,不能再重新的发生变化
// 6) 或者当executor 发生异常的时候 也会触发promise的失败

const Promise = require("./promise.js");

const promise = new Promise((resolve, reject) => {
   
   
  console.log("executor");
  resolve("ok"); //resolve 与 reject 可以改变promise的状态
  reject("error");
});

promise.then(
  (data) => {
   
   
    console.log(data, "success");
  },
  (err) => {
   
   
    console.log(err, "reject");
  }
);

// console.log(promise);

但是上述代码有个问题:promise 调用 then 方法时可能当前的 promise 并没有成功,而是处于pending 状态。

例如:

const Promise = require("./promise.js");

const promise = new Promise((resolve, reject) => {
   
   
  console.log("executor");

  setTimeout(() => {
   
   
    // resolve("ok"); //resolve 与 reject 可以改变promise的状态
    reject("error");
  }, 1000);
});

promise.then(
  (data) => {
   
   
    console.log(data, "success1");
  },
  (err) => {
   
   
    console.log(err, "reject1");
  }
);

promise.then(
  (data) => {
   
   
    console.log(data, "success2");
  },
  (err) => {
   
   
    console.log(err, "reject2");
  }
);

// console.log(promise);

他并没有执行回调函数 ,但是这个问题很好解决 只要一个简单的发布订阅模式,如果当前状态是pending状态是,先将所有的回调函数储存起来,待等到相应状态依次调用储存的回调函数。 代码如下:

const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
   
   
  constructor(executor) {
   
   
    this.state = PENDING; // new Promise 后 默认状态为pending
    this.value = undefined; //成功的原因
    this.reason = undefined; //失败的原因

    // 发布订阅模式 
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    // promise调用then的时候 可能状态依旧是pending,那么我们需要将回调先存放起来
    // 等待过一会调用resolve时触发 onResolvedCallbacks 执行
    // 等待调用 reject时触发 onRejectedCallbacks 执行

    const resolve = (value) => {
   
   
      //只有状态是pending的时候 才可以修改状态
      if (this.state === PENDING) {
   
   
        this.state = FULFILLED;
        this.value = value;
        this.onResolvedCallbacks.forEach((cb) => cb());
      }
    };

    const reject = (reason) => {
   
   
      if (this.state === PENDING) {
   
   
        this.state = REJECTED;
        this.reason = reason; //先赋值 后执行 
        this.onRejectedCallbacks.forEach((cb) => cb());
      }
    };
    // 调用executor 会自动传入resolve 与 reject
    try {
   
   
      //或者当executor 发生异常的时候 也会触发promise的失败
      executor(resolve, reject);
    } catch (error) {
   
   
      reject(error);
    }
  }
  then(onFulfilled, onRejected) {
   
   
    if (this.state === FULFILLED) {
   
   
      onFulfilled(this.value);
    }

    if (this.state === REJECTED) {
   
   
      onRejected(this.reason);
    }

    if (this.state === PENDING) {
   
   
      this.onResolvedCallbacks.push(() => {
   
   
        onFulfilled(this.value);
      });
      this.onRejectedCallbacks.push(() => {
   
   
        onRejected(this.reason);
      });
    }
  }
}

module.exports = Promise;

此时结果:

3-实现promise的链式调用

链式调用可以解决地狱回调问题:

  1. promise 成功和失败的回调的返回值可以传递到外层的下一个 then
  2. promise 返回值情况
  • 如果返回的是 promise 的情况:会采用 promise 的状态,决定下一步是走成功还是失败
  • 如果返回的是普通的值的话:传递到下一次的成功中(不是抛出错误(throw new Error)也不是 promise 就是普通值)
  • 如果返回的是抛出错误的情况:一定会走到下一次的失败中
  1. 错误处理:如果离自己最近的 then 没有错误处理(没有写错误函数)会向下找
  2. 每次执行完 <font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">promise.then</font> 方法后返回的都是一个新的 promise (promise 一旦成功或者失败都不能去修改状态)
// (1) then链的特点:当then中成功和失败的回调 返回的是一个promise, 内部会解析这个promise, 并将结果传递到外层的下一个then
// (2) 下一次then走成功还是失败, 取决于当前promise的状态
// (3) 如果成功和失败返回的不是一个promise, 那么这个结果直接传递到下一个成功
// (4) 如果成功和失败返回的抛出异常了, 那么这个异常会直接传递到下一个失败
// (5) 让一个promise(then) 变成失败有两种方式, 一种是直接抛出异常, 一种是返回一个失败的promise
readFile(path.resolve(__dirname, "a.txt"), "utf-8")
  .then((data) => {
   
   
    return readFile(data, "utf-8"); // 返回的是一个promise
  })
  .then(
    (data) => {
   
   
      console.log(data, "success");
    },
    (err) => {
   
   
      console.log(err, "error");
      return true;
    }
  )
  .then((data) => {
   
   
    console.log(data, "success2");
    throw new Error("error");
  })
  .then((data)=>{
   
   },(err)=>{
   
   
    console.log(err, "error2");
  })

分别对应返回值的三种情况:

下面来实现一下promise的链式调用:

3-1 实现链式调用里普通值处理

如果返回的是普通的值的话:传递到下一次的成功中(不是错误也不是 promise 就是普通值)resolvePromise函数会做这个处理 后续会写到 先了解一下这个流程。

3-2 实现链式调用里返回的是Promise处理

具体可以参考这个:https://promisesaplus.com/

下面是这两种情况的代码实现:resolvePromise函数统一处理这两种情况,第三种情况是返回的是抛出错误的情况:一定会走到下一次的失败中 (直接有trycatch捕获到error后直接调用reject(e)即可)

const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

// 为了让所有人promise 可以互相调用 所有的promise都需要这个规则
function resolvePromise(promise2, x, resolve, reject) {
   
   
  console.log(promise2,x);
}

class Promise {
   
   
  constructor(executor) {
   
   
    this.state = PENDING; // new Promise 后 默认状态为pending
    this.value = undefined; //成功的原因
    this.reason = undefined; //失败的原因

    // 发布订阅模式
    // 存放成功回调
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = []; // 存放失败回调
    // promise调用then的时候 可能状态依旧是pending,那么我们需要将回调先存放起来
    // 等待过一会调用resolve时触发 onResolvedCallbacks 执行
    // 等待调用 reject时触发 onRejectedCallbacks 执行

    const resolve = (value) => {
   
   
      //只有状态是pending的时候 才可以修改状态
      if (this.state === PENDING) {
   
   
        this.state = FULFILLED;
        this.value = value;
        this.onResolvedCallbacks.forEach((cb) => cb());
      }
    };

    const reject = (reason) => {
   
   
      if (this.state === PENDING) {
   
   
        this.state = REJECTED;
        this.reason = reason; //先赋值 后执行
        this.onRejectedCallbacks.forEach((cb) => cb());
      }
    };
    // 调用executor 会自动传入resolve 与 reject
    try {
   
   
      //或者当executor 发生异常的时候 也会触发promise的失败
      executor(resolve, reject);
    } catch (e) {
   
   
      reject(e);
    }
  }

  // promise 必须提供 then 方法来存取它当前或最终的值或者原因。
  // then 接收两个参数:onFulfilled 和 onRejected
  then(onFulfilled, onRejected) {
   
   
    //new Promise promise会立即执行
    let promise2 = new Promise((resolve, reject) => {
   
   
      if (this.state === FULFILLED) {
   
   
        setTimeout(() => {
   
     // 此时promise2还没有定义 不能将其传进去 所以抱一个setTimeout方法
          try {
   
   
            let x = onFulfilled(this.value); // promise返回的是一个普通值的话 传递到下一个promise中
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
   
   
            //如果返回的是错误的情况:一定会走到下一次的失败中
            reject(e);
          }
        });
      }

      if (this.state === REJECTED) {
   
   
        setTimeout(() => {
   
   
          try {
   
   
            let x = onRejected(this.reason); // promise返回的是一个普通值的话 传递到下一个promise中
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
   
   
            reject(e);
          }
        });
      }

      if (this.state === PENDING) {
   
   
        this.onResolvedCallbacks.push(() => {
   
   
          setTimeout(() => {
   
   
            try {
   
   
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
   
   
              reject(e);
            }
          });
        });
        this.onRejectedCal
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值