【js进阶】手撕Promise,一码一解析 包懂!

一、分析Promise使用

  MDN中关Promise的定义如下:Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。 一个 Promise 对象代表一个在这个 promise 被创建出来时不一定已知的值。它让您能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。 这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者。
  一个 Promise 必然处于以下几种状态之一:
    待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
    已兑现(fulfilled): 意味着操作成功完成。
    已拒绝(rejected): 意味着操作失败。
  待定状态的 Promise 对象要么会通过一个值被兑现(fulfilled),要么会通过一个原因(错误)被拒绝(rejected)。当这些情况之一发生时,我们用 promise 的 then 方法排列起来的相关处理程序就会被调用。如果 promise 在一个相应的处理程序被绑定时就已经被兑现或被拒绝了,那么这个处理程序就会被调用,因此在完成异步操作和绑定处理方法之间不会存在竞争状态

new Promise((resolve, reject) => {
  //异步操作
  //···
  //执行完后调用resolve和reject输出两种不同结果
  if (true) {
    resolve("res");
  } else {
    reject("err");
  }
})
  .then((res) => { //then接受resolve中的结果
    console.log(res);
  })
  .catch((err) => { //catch接受reject中的结果
    console.log(err);
  });

Promise的使用分为三步:

  • 1、新建Promise实例,即通过new实现,同时接受一个函数参数,函数参数中接受resolve和reject两个形参(实质上也是函数);
  • 2、新建的Promise实例接受的函数参数中就是要执行的异步代码,并且用resolve和reject对异步结果进行调用输出;
  • 3、新建的Promise实例可以调用then和catch方法对异步结果进行接受和处理;

上述新建实例代码可以转化为:

function fn(resolve, reject) {
  //异步操作
  //···
  //执行完后调用resolve和reject输出两种不同结果
  if (true) {
    resolve("res");
  } else {
    reject("err");
  }
}

let p = new Promise(fn);

p.then((res) => { //then接受resolve中的结果
    console.log(res);
  })
  
p.catch((err) => { //catch接受reject中的结果
    console.log(err);
  });

上述中的使用者就是then和catch,结合代码中的使用方式,简单来说就是Promise中执行异步操作,then和catch只会在异步执行完后才会接到返回结果继续执行!
在这里插入图片描述

二、手撕Promise

  了解了Promise的定义和使用步骤后,接下来直接手撕Promise的实现,直接上实现Promise的代码(内涵大量注释,基本一句一解释,但是逻辑还是得第三部分来讲):

// 定义promise中的三种状态
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";

// 定义promise的类
class myPromise {
  //class的构造函数,接受新建实例时的参数:executor在promise中是一个函数
  constructor(executor) {
    //初始化该class中的初始状态
    this.status = STATUS_PENDING;
    //定义class中成功(res)和失败(err)时的变量值
    this.res = "";
    this.err = "";

    //promis异步中最重要的异步,定义成功和错误函数存储的数组,存放异步时还没有执行的操作
    this.onResCallbacks = [];
    this.onErrCallbacks = [];

    //定义该构造函数constructor定义域中的变量resolve
    let resolve = (res) => {
      // 首先判断该class中的状态,只有状态为pending时才能转化class转态为fulfilled或者rejected
      if (this.status === STATUS_PENDING) {
        //修改class的转态为fulfilled,也就表示不会转进行其他转态的转化了
        this.status = STATUS_FULFILLED;
        //将成功(resolve)状态下的值赋给class的成功返回res
        this.res = res;
        //此时状态由pending转为fulfilled,执行之前在then中存放的需要执行的异步操作,promise的then中参数res接受结果
        this.onResCallbacks.forEach((fn) => {
          fn();
        });
      }
    };

    //定义该构造函数constructor定义域中的变量reject
    let reject = (err) => {
      // 首先判断该class中的状态,只有状态为pending时才能转化class转态为fulfilled或者rejected
      if (this.status === STATUS_PENDING) {
        //修改class的转态为rejected,也就表示不会转进行其他转态的转化了
        this.status = STATUS_REJECTED;
        //将失败(reject)状态下的值赋给class的失败返回err
        this.err = err;
        //此时状态由pending转为rejected,执行之前在catch中存放的需要执行的异步操作,promise的catch中参数err接受结果
        this.onErrCallbacks.forEach((fn) => {
          fn();
        });
      }
    };

    //按照promise中的逻辑,在调用时就立即执行了,所以在手写的myPromise创建构造函数constructor时就执行executor
    try {
      //执行参入的函数,并将上述定义的resolve和reject作为参数传入
      executor(resolve, reject);
    } catch (err) {
      //报错时调用失败的状态函数
      reject(err);
    }
  }
  
  //在class中定义promise的成功状态接收函数then,按照promise逻辑,then中传入的一般都是一个函数
  then(onRes = () => {}) {
    //如果是异步的,此时在constructor中status的状态还没变成fulfilled,所以会跳过onRes调用,没有返回
    if (this.status === STATUS_FULFILLED) {
      onRes(this.res);
    }
    //但是我们将此时的异步放入数组存放
    if (this.status === STATUS_PENDING) {
      this.onResCallbacks.push(() => onRes(this.res));
    }
    //这步操作保证了then和catch能够在同级一起"."调起,当then上述操作完后,返回class实例,便可以接在后面继续调用catch
    return this;
  }

  //在class中定义promise的失败状态接收函数catch,按照promise逻辑,catch中传入的一般都是一个函数
  catch(onErr = () => {}) {
    //如果是异步的,此时在constructor中status的状态还没变成rejected,所以会跳过onErr调用,没有返回
    if (this.status === STATUS_REJECTED) {
      onErr(this.err);
    }
    //但是我们将此时的异步放入数组存放
    if (this.status === STATUS_PENDING) {
      this.onErrCallbacks.push(() => onErr(this.err));
    }
     //这步操作保证了then和catch能够在同级一起"."调起,当catch上述操作完后,返回class实例,便可以接在后面继续调用then
    return this;
  }
}

//调用自己手写的promise
new myPromise((resolve, reject) => {
  console.log("进入了手写的promise");
  //用setTimeOut模拟异步操作
  setTimeout(() => {
    if (false) {
      resolve("输出成功结果resolve");
    } else {
      reject("输出失败结果reject");
    }
  }, 2000); //按照js的特性,此时不会等待异步完成,直接调用then或者catch
})
  .then((res) => {
    console.log("then:", res);
  })
  .catch((err) => { //return this具体作用体现在这里
    console.log("catch:", err);
  });

三、实现逻辑详析

前提知识点:关于ES6中class的用法和相关属性(ES6阮一峰class讲解

在这里插入图片描述

欢迎大家关注本人的微信公众号,微信公众号将不定期发送相应学习文章和教程

微信号:chiyizao

或者微信公众号搜索:
迟亦早
  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值