原生JS实现Promise(详解)

2023-05-16

摘要

首先呢,Promise是异步中比较重要的知识点,学习的最好方法就是掌握它的基本原理。
所以这一篇主要说一下如何用JS来实现一个自己的promise。

构造函数

首先我们来看一下我们是如何使用promise的,我们在实例化对象是这么使用的:

    let p1 = new Promise((resolve, reject) => {
      let random = Math.floor(Math.random() * 10);
      if (random > 4) {
        resolve('sucess')
      } else {
        reject('erro')
      }
    })

所以我们在创建我们自己的类要考虑到如何使用这个参数。


我们来看一下, new Promise 的时候传了一个回调函数,在这个回调函数中的代码应该是被立即执行的。

而在这个回调函数中,还带有这两个参数resolve和reject(也是回调函数)。

所以在我们的构造函数中,应该是有这两个函数resolve和reject(暂时先不管这两个函数是做什么的)。


我们知道promise是有三个属性的:

pending : 待定
fulfilled : 对应resolve函数
rejected : 对应reject函数

并且状态一旦改变就不能再更改了。
所以我们的构造函数之中应该有表示当前promise状态的属性


我们知道不管使用resolve还是reject都会传入一个res变量,作为结果值,所以我们在用一个属性来保存resolve和reject的结果值

最后我们可以设计出这样的构造函数:

function Mypromise (config) {
  this.status = 'pending';
  this.res = ''
  let resolve = (data) => {
    this.status = 'fulfilled';
    this.res = data
  }
  let reject = (data) => {
    this.status = 'rejected';
    this.res = data
  }

  config(resolve, reject)
}

then 和 catch方法

我们先来回顾一哈怎么使用这两个方法:

    p1
      .then(res => {
        console.log(res);
      })
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.log(err);
      })

上面的代码我们可以看到,then和catch方法,都接受了一个回调函数
而这个回调函数的参数也就是我们之前定义的this.res

所以我们可以想到这么做:

Mypromise.prototype.then = function (config) {
  if (this.status == 'fulfilled') {
    config(this.res)
  }
}

Mypromise.prototype.catch = function (config) {
  if (this.status == 'rejected') {
    config(this.res)
  }
}

但是这种方法不能实现链式调用,就是不能连着使用then方法。
但是如果我想实现出这个模式,我们应该在then方法下回一个对象,而这个对象正常来讲就是this
所以我们可以直接返回this吗,看下面这个情况。

p1
  .then(res => {
    console.log(res);
    return new Promise((resolve, reject) => {
      resolve('1111')
    })
  })
  .then(res => {
    console.log(res);
  })
  .catch(err => {
    console.log(err);
  })

在then方法下如果返回了一个新的promise的话,我们就不能直接在then方法里面直接返回this了。

所以我们应该先判断then的回调函数是否返回了新的对象,如果没有才返回当前then的this对象。

Mypromise.prototype.then = function (config) {
  if (this.status == 'fulfilled') {
    var res = config(this.res)
  }
  return res || this;
}

Mypromise.prototype.catch = function (config) {
  if (this.status == 'rejected') {
    var res = config(this.res)
  }
  return res || this;
}

解决异步问题

上面的代码,似乎看着没有什么问题了,但是如果我这么写的话:

    let p2 = new Mypromise((resolve, reject) => {
      setTimeout(() => {
        reject('p2 resolve')
      }, 1000);
    })

问题就大大的出来了,为什么呢? 因为我在p2.then的时候,定时器没有跑完,所以p2的状态现在还是pending,根本不会走下去。

这里面我们用一种经典的解决模式,在我写之前的axios和路由也经常可以看到。

在then方法中,如果当前状态为pending(这句话很重要o),我们就把当前的回调函数保存下来(不一定是一个,有可能是多个then,所以我们采用数组保存)。

那我们保存起来什么时候用呢?当然是在定时器结束后用!那定时器什么时候结束呢?当然是当前promise状态改变的时候,所以,我们在resolve和reject方法之中,要将这些方法进行调用!!!

所以我们要修改构造函数:

function Mypromise (config) {
  this.status = 'pending';
  this.res = '';
  this.saveResolve = [];
  this.saveReject = [];
  let resolve = (data) => {
    if (this.status == 'pending') {
      this.status = 'fulfilled';
      this.res = data
      this.saveResolve.forEach(val => {
        val(this.res)
      })
    }
  }
  let reject = (data) => {
    if (this.status == 'pending') {
      this.status = 'rejected';
      this.res = data
      this.saveReject.forEach(val => {
        val(this.res)
      })
    }
  }
  config(resolve, reject);
}

然后再修改我们的then和catch方法:

Mypromise.prototype.then = function (config) {
  if (this.status == 'pending') {
    this.saveResolve.push(config);
  }
  if (this.status == 'fulfilled') {
    var res = config(this.res)
  }
  return res || this;
}

Mypromise.prototype.catch = function (config) {
  if (this.status == 'pending') {
    this.saveReject.push(config)
  }
  if (this.status == 'rejected') {
    var res = config(this.res)
  }
  return res || this;
}

这样关于异步的问题我们就解决了。

all和race方法

还是老样子,在写之前我们先回顾一下是怎么用的:

    Mypromise.all([p2, p3, p4])
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.log(err);
      })

    Mypromise.race([p2, p3, p4])
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.log(err);
      })

那我们知道,二者都死以一个数组作为参数,这里面我门就不考虑其他的情况了,我就当数组里面全是promise对象了。。。

二者的区别在于:
all当所有的promise都执行完,并且状态都为fulfilled,all方法返回的promise为fulfilled,否则为rejected。

race第一个出现结果的promise对象就是race放回的promise的结果。


现在我们来想一下all方法如何来实现,我们拿到了数组参数之后,一定是要遍历一遍的。

然后对于每一个元素都调用then方法和catch方法。

then方法要有一个结果数组保存每个promise的结果值。
我们可以用一个计数器来计算then方法的调用次数,如果计数器的大小等于数组长度,那么就证明所有的promise全部都是fulfilled,可以返回结果数组。

catch方法只要是被调用了一次,那么直接返回结果,不多bb,直接返回

最后记住要把新的promise返回o。

Mypromise.all = function (arr) {
  let result = [];
  let count = 0;
  let promise = new Mypromise((resolve, reject) => {
    for (var i = 0; i < arr.length; i++) {
      arr[i]
        .then(res => {
          result.push(res);
          count++;
          if (count == arr.length) resolve(result);
        })
        .catch(err => {
          reject(err)
        })
    }
  })

  return promise
}

race的方法的话,实现起来可能就更简单了,不管那个promise的then方法还是catch方法触发了,直接返回结果:

Mypromise.race = function (arr) {
  let promise = new Mypromise((resolve, reject) => {
    for (var i = 0; i < arr.length; i++) {
      arr[i]
        .then(res => {
          resolve(res);
        })
        .catch(err => {
          reject(err)
        })
    }
  })

  return promise
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

原生JS实现Promise(详解) 的相关文章

  • knex.js 查询“承诺”何时执行/解析?

    对于正常的承诺 一旦当前代码停止 承诺就会尽快解决 考虑以下伪代码 const p SomePromiseDoingLoad calculatethings await sleepPromise during sleep the promi
  • 在 Google Apps 脚本中异步运行函数

    我正在制作一个调用 GAS 函数的 Slack 机器人 一切正常 只是 Slack 显示错误消息 因为它在调用 API 时只等待 3 秒响应 任何人都可以帮助我弄清楚如何异步运行 everyDay2 以便我可以在完成之前返回响应 我尝试过
  • 如何使用带有 Promise 的 Socket.IO?

    作为持续努力的一部分 我正在将当前的回调技术更改为使用承诺blue bird承诺图书馆 我也想用 Socket IO 来实现这项技术 如何使用带有承诺而不是回调的 Socket IO 有没有使用 Socket IO 的标准方法 有官方解决方
  • NodeJS My SQL 查询与 Chain Promise

    我有3个函数 我想逐步调用这个函数 例如当我调用第一个函数并获取结果时 我必须调用第二个函数并传递从第一次调用返回的参数 在完成第二个调用后 我必须调用第三个函数并传递从第二个函数返回的参数 1 getCategory function b
  • 承诺取消仍在触发履行功能

    这是我的代码 var promiseResult new BBpromise function resolve reject console log 1 some actions setTimeout function resolve 20
  • JS/ES 离线并存储 Promise 供以后使用

    我如何才能取消这个 示例 承诺 即 将承诺归因于变量以供以后传递 使用 this example promise works OK just test with x 0 and x lt gt 0 var x 0 new Promise a
  • Javascript for 循环 Promise

    我有一个像这样的网址数组 var urls www google com www yahoo com 我想循环遍历 url 并在循环内执行异步任务 并且在异步任务完成之前不会继续执行下一个项目 我知道你可以通过承诺做到这一点 但我在这方面遇
  • 为什么这个 readline 异步迭代器无法正常工作?

    这是一个更大流程的一部分 我在节点 v14 4 0 中将其提炼为最小的 可重现的示例 在此代码中 它从内部不输出任何内容for loop 我在控制台中只看到这个输出 before for loop finished finally done
  • 有条件的承诺

    在我的脚本中 我需要检索一个字典以将编码值转换为名称 ajax retrieve dictionary done function dictionary convert encoded values into names done func
  • 我如何知道哪些处理程序在 Promise 中抛出错误?

    假设我有如下承诺 p then Task1 then Task2 then Task3 catch errorHandler When Task2遇到错误 我怎么知道错误来自Task2 in catch 每个人 我自己研究了演示代码 希望大
  • 异步等待获取未定义。如何处理?

    我目前正在学习async await fetch我创建了以下示例来帮助我学习 下面的工作示例 从公共 API 获取三个随机 json 记录 提取出url从每次返回json 创建三个img元素 追加三img文档正文的元素 请注意promise
  • AngularJS - 绑定/监视返回承诺的函数

    我已经发布an issue https github com angular angular js issues 3503在 AngularJS github 上 但它似乎没有得到太多关注 而且我无法自己修复它 因为这是一个相当低级的问题
  • 如何将 Promise 与 Angular.js 和 $resource 与查询字符串一起使用?

    使用需要传递给它的查询字符串 参数的资源来处理 Angular js 中的承诺的最佳方法是什么 我已经看到 q 的工作在工厂 控制器和路由器中处理 但我不确定在涉及参数时如何处理它 所以如果这是工厂 angular module Anima
  • 如何确保 Jest 在“unhandledRejection”上失败?

    我们的单元测试在持续交付管道的容器中运行 有时 我们不会在单元测试中处理拒绝 但是 我认为这是不正确的 并且在我看来管道应该失败 我怎样才能确保当我执行时jest并且在测试期间unhandledRejection事件发生 jest 会错误退
  • 蓝鸟承诺解析(数据)在客户端代码中未定义

    希亚斯 我有一个简单的应用程序 客户端期望得到一个承诺作为结果 但是在调用resolve 方法时 承诺不断返回未定义的结果 客户端代码 UsersRepo findOneAsync id id then function err resul
  • 如何返回许多 Promise 并在执行其他操作之前等待所有 Promise

    我有一个循环 它调用一个异步执行操作的方法 这个循环可以多次调用该方法 在这个循环之后 我有另一个循环 仅当所有异步工作完成时才需要执行 所以这说明了我想要的 for i 0 i lt 5 i doSomeAsyncStuff for i
  • 为什么我应该使用 HttpClient 而不是 fetch?

    Angular 2 介绍HttpClient它发出一个 HTTP 请求并将它们发送到一个 RxJS observable 中 我的问题是为什么我会选择使用HttpClient s API https angular io guide htt
  • 处理获取响应的正确方法是什么

    我有以下用于处理 Magento 2 REST API 的代码 return new Promise resolve reject gt fetch uri method headers body JSON stringify data t
  • Node.js 承诺、异步或只是回调

    你能向初学者解释一下 承诺 异步 和 回调 之间的区别吗 这些术语如何相互关联 这些是同一件事吗 不同的东西 我什么时候使用哪个 异步是启动计算并提供函数或注册处理程序的一般设计模式 该处理程序最终将在计算完成时使用计算结果进行调用 而不是
  • NodeJS 获取承诺回调挂起

    我有这个代码 fetch url then response gt const json response json console log simplest possible fetch json json where 在控制台中我得到

随机推荐

  • Linux(Ubuntu)配置Cuda,Pytorch,Anaconda

    近期需要在Linux xff08 Ubuntu20 04 xff09 上运行一个工程 xff0c 需要搭建相关环境 xff0c 这是首次在Linux系统上完成anaconda xff0c cuda xff0c 及Pytorch的下载与配置
  • Visual Studio配置OpenGL

    近期工作中需要用到OpenGL 而之前一直是用Opencv工作 xff0c 这就需要在VS上配置OpenGL 因为是首次在VS上配置OpenGL xff0c 以备自己和有需要的小伙伴不时之需 我的VS是2022版的 xff0c 但配置流程各
  • Pycharm终端问题: python : 无法将“python”项识别为 cmdlet、函数、脚本文件或可运行

    发现这个问题的起因是我打算尝试用Django练习做网站 xff0c 需要在Pycharm终端输入一些命令以运行脚本 xff0c 我的Pycahrm配置了anaconda xff0c 但在终端运行命令时一直报错 xff1a python 无法
  • Python:把列表内容按行数写入txt

    事情的起因是我需要把一个元素全为数字的列表按固定列数写入txt文件 xff0c 也就是每行几个元素 xff0c 用逗号隔开 看了一些网上的分享觉得都不太合适 xff0c 于是自己想了一个办法 xff0c 一行代码解决 xff0c 废话少说
  • 解决Git提交代码报错: ERROR: commit xxxxx: missing Change-Id in message footer

    在近期的工作中完成代码修改提交代码时Git报错并提示提交不成功 xff0c 具体错误如下 xff1a 原因是Change Id缺失 至于解决方法 xff0c Git在报错时已经提示了 xff0c 如下图黄框所示 xff1a 首先 xff0c
  • 如何实现用串口助手实时绘制16位数据波形图

    先和大家kuan两句 xff0c 哈哈 因为之前参加智能车想用波形显示来调节PID xff0c 找了很多工具也没有成功 xff0c 心里也知道串口一次就是只能发送八位数据 xff0c 很多时候可以用字符显示16位的 xff0c 但是就不是数
  • STM32之中断方式实现串口通信

    中断方式实现串口通信 一 创建项目二 编写代码三 运行四 总结 一 创建项目 创建一个STM32f103c8的STM32CubeMX项目 xff1a SYS设置 xff1a RCC设置 xff1a 时钟树设置 xff0c 输入72后回车 x
  • 1.(1)数据结构之链表-typedef的用法

    本人坚持更新C语言 xff0c 数据结构 xff0c 操作系统知识 xff0c 可以收藏 xff0b 关注随时了解 x1f61c x1f61c x1f61c 目录 我们在之前学习结构体的时候 xff0c 是如何定义结构体的呢 xff1f t
  • gazebo 中创建含有二维码的墙的模型

    1 新建空白墙的模型 在gazebo中添加一个Edit gt Building Editor xff0c 生成sdf文件 xff0c 放在 gazebo models文件夹下 如图Untitled1 编辑model sdf文件 xff0c
  • 在vscode中开发arduino编译巨慢解决办法

    每次在vscode中 编译Arduino花费的时间巨长 xff0c 等的好烦 xff0c 仔细一看每次在Arduino 输出控制台上会出现一个警告 Warning Output path is not specified Unable to
  • 工作空间中的devel和build文件夹可以删掉

    工作空间中的devel和build文件夹可以删掉 xff0c 再cmake就可以产生
  • IOT的核心—无线通讯模块

    文章目录 前言一 IOT是什么 xff1f 1 IOT的运用 1 智能家居 2 无线控制 2 IOT总结 三 如何从互联网转换为物联网简述芯百特的CB2401与CB2402 1 CB2401介绍内部结构与管脚图产品应用评估板原理图 2 CB
  • STM32F103C8T6 串口3(USART3) 只能发不能收

    问题原因 xff1a 今天因为上述问题 困扰一天 xff01 最后发现是 PB8 9 xff08 配置输出 xff09 硬件短路了 xff01 问题现象 xff1a STM32F103C8T6 串口3 USART3 只能发不能收 xff01
  • eNSP第四篇:IP地址,逻辑接口,接口类型,三层路由接口,二层路由接口

    IP地址 xff0c 逻辑接口 xff0c 接口类型 xff0c 三层路由接口 xff0c 二层路由接口 私有IP地址的范围 IP范围 默认掩码 A类 10 0 0 0 10 255 255 255 255 0 0 0 B类 172 16
  • IMU及磁力计AHRS系统控制(一):传感器物理实现原理

    AHRS系统前言 AHRS是 Attitude and heading reference system 的英文缩写 xff0c 百度对此的解释是 航姿参考系统 xff0c 按笔者比较浅薄的理解就是在计算平台上通过算法处理一套部署在被控对象
  • 归并排序(C语言)详解

    记录学习第五天 今天记录一下归并排序 xff0c 因为在csdn里面没有找到特别清楚的解析 xff0c 所以想自己写的认真一点 xff0c 也查阅了一些资料 xff0c 通过这篇博客记录一下 xff1b 归并排序 xff0c 光看字面 xf
  • HTML的块级元素(常用整理)

    emmm xff0c 最近想整理复习一下前端的基础 xff0c 最开始的HTML想了好久也没想好怎么写 xff0c 最后也是决定以行块这样整理 xff0c 再在后面补充吧 说到HTML xff0c 什么是HTML呢 xff1f 什么是 HT
  • JS实现快速排序(代码+讲解)

    OK xff0c 排序这一个篇章也快要结束了 这一篇主要说的是快速排序 xff0c 说的方式主要还是先说原理 xff0c 然后再用代码来进行实现 所谓快速排序 xff0c 就是分为三步走 xff1a 第一步 xff1a 选择第一个数字分离出
  • Object.defineProperty方法(详解)

    OK xff0c 这一篇主要想说一下Object defineProperty这个方法 这个方法也是Vue数据双向绑定原理的常见面试题 所以也是有必要好好掌握的哦 首先我们知道JS中是支持面向对象编程的 xff0c 也是有着对象和类这样的概
  • 原生JS实现Promise(详解)

    摘要 首先呢 xff0c Promise是异步中比较重要的知识点 xff0c 学习的最好方法就是掌握它的基本原理 所以这一篇主要说一下如何用JS来实现一个自己的promise 构造函数 首先我们来看一下我们是如何使用promise的 xff