摘要

首先呢,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 = datathis.saveResolve.forEach(val => {val(this.res)})}}let reject = (data) => {if (this.status == 'pending') {this.status = 'rejected';this.res = datathis.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
}

原生JS实现Promise(详解)相关推荐

  1. JS 的 Promise详解

    @[TOC](JS 的 Promise详解)欧诺个鱼 1.概念 ES 6 开始支持 Promise. Promise 对象用于一个异步操作的最终完成(包括成功和失败)及结果值的表示.简而言之,就是处理 ...

  2. flv.js php,flv.js的使用详解

    这次给大家带来flv.js的使用详解,使用flv.js的注意事项有哪些,下面就是实战案例,一起来看一下. Bilibili相信大家都不会陌生,而 Flv.js 就是由 bilibili 网站开源的 H ...

  3. js_long.php,protobuf.js 与 Long.js的使用详解

    这次给大家带来protobuf.js 与 Long.js的使用详解,是急用protobuf.js 与 Long.js的注意事项有哪些,下面就是实战案例,一起来看一下. protobuf.js的结构和w ...

  4. 《Node.js开发实战详解》学习笔记

    <Node.js开发实战详解>学习笔记 --持续更新中 一.NodeJS设计模式 1 . 单例模式 顾名思义,单例就是保证一个类只有一个实例,实现的方法是,先判断实例是否存在,如果存在则直 ...

  5. highlight.js css,JS库之Highlight.js的用法详解

    下载到本地后,新建个页面测试 1.在head中加入css和js的引用 highlight hljs.initHighlightingOnLoad(); 2.添加对应要显示的内容 # 读取文件内容 de ...

  6. js排序算法详解-归并排序

    js系列教程5-数据结构和算法全解 js排序算法详解-归并排序 归并排序其实可以类比二分法,二分法其实就是二等分的意思,简而言之就是不断和新序列的中间值进行比较.归并排序似乎有异曲同工之妙,什么意思呢 ...

  7. js排序算法详解-基数排序

    全栈工程师开发手册 (作者:栾鹏) js系列教程5-数据结构和算法全解 js排序算法详解-基数排序 其实基数排序和桶排序挺类似的,都是找一个容器把属于同一类的元素装起来,然后进行排序.可以把基数排序类 ...

  8. js排序算法详解-桶排序

    全栈工程师开发手册 (作者:栾鹏) js系列教程5-数据结构和算法全解 js排序算法详解-桶排序 一看到这个名字就会觉得奇特,几个意思,我排序还要再准备几个桶不成?还真别说,想用桶排序还得真准备几个桶 ...

  9. js排序算法详解-计数排序

    全栈工程师开发手册 (作者:栾鹏) js系列教程5-数据结构和算法全解 js排序算法详解-计数排序 计数排序就是遍历数组记录数组下的元素出现过多次,然后把这个元素找个位置先安置下来,简单点说就是以原数 ...

  10. js排序算法详解-堆排序

    全栈工程师开发手册 (作者:栾鹏) js系列教程5-数据结构和算法全解 js排序算法详解-堆排序 这种排序方式呢,理论性太强,看动图的时候满脸写着懵逼,多看几遍似乎明白了编者的意图,但是要把这种理论的 ...

最新文章

  1. static和global的区别
  2. Priority VS Bandwidth
  3. VMware(VMDebugger)导致VS2010启动慢的解决办法
  4. 趁热打铁-一次.Net WebService的性能优化之旅
  5. “注册编辑已被管理员停用”之解决办法
  6. java判断字符串的值是否为0或者为空
  7. 英国法院裁定GCHQ黑客发动网络攻击并不侵犯人权
  8. spring——autowire自动注入
  9. R语言中%in%的作用
  10. Mac 开发 Hue
  11. 软件项目管理 2.2.项目招投标流程
  12. 文件共享服务器如何提高网速,局域网共享文件传输速度很慢怎么办
  13. 2022 Ubuntu国内流畅观看Cousera视频最简教程
  14. VUE-鼠标移入到目标区域变成小手模样
  15. 参加项目管理培训的一些体会
  16. python对矩阵部分求和_python – numpy求和矩阵 – 按索引排
  17. 速腾聚创激光雷达部署
  18. 【小程序】小程序起步、WXML模板语法和WXSS模板样式
  19. 163VIP邮箱如何注册?163VIP企业邮箱申请介绍!
  20. 我们对Google的排名算法到底了解多少?

热门文章

  1. 视频流媒体服务器如何用OBS推流录屏或直播?
  2. 高斯径向基函数的理解
  3. Am335x 平台上GSM 3G/4G modem的一些硬件和软件的杂事
  4. 防火墙控制Docker端口开放与关闭
  5. 1085: [SCOI2005]骑士精神
  6. 洛谷题单 算法1-3 暴力枚举
  7. cadence SPB17.4 - allegro - 手工放置原理图没有的封装
  8. Android修改ro.debuggable 的第五种方法
  9. P02014145 杨志豪 信息论作业
  10. 群签名技术的理解和总结