使用ES6中Class实现手写PromiseA+,完美通过官方872条用例
长
篇
预
警
!
🧡最近忙着事业,又忙着晋升学习,还要搬家,好久没有输出了,不过还是要抽出边边角角的时间,分享一些好的内容,如果对你有所帮助,笔个芯再走吧🧡
废话不多说,相信大多数人,一提到Promsie
就能条件反射想到回调地狱
,看了ES6官方晦涩难懂的解释,感觉一脸懵逼,害怕面试的时候被问到,答起来完全不知道自己在说什么,那么今天就来帮你解决掉这一大难题。
长篇预警!有点长,可以选择性观看。本篇主要带领你手写一个Promise源码,学完你就会发现:Promise没有你想象中的那么难,如果对Promise源码不是很清楚,还是推荐从头看,相信你认真从头看到尾,并且去实际操作了,肯定会有收获的。主要是代码部分有点多,不过好多都是重复的,不必担心
Promise出现的原因
promise出现的原因,说白了也就是promise解决了什么问题,这里我就简单说点吧:
Promise 是异步编程的一种解决方案,比传统的解决方案回调函数和事件更合理和更强大,Promise的出现主要解决以下两个问题:
- 回调地狱: 某个异步操作需要等待之前的操作完成在继续执行, 当这样的需求多了以后, 使的代码进入无尽的嵌套,可读性降低不好维护
- 异步和同步之间的联系问题:当一个同步操作需要等待多个异步操作的结果, 这样会使得代码的逻辑变得相对来说比较复杂
myPromise的实现要点
- Promise 就是一个类,实际上就是ES6提供的一个新的构造函数,
- 通过
new
创建实例,接收一个函数作为参数,并且该函数中的代码默认是同步代码,会立即执行 - Promise 有三种状态,
成功resolved
,失败rejected
,等待pedding
- Promise 状态一旦发生改变,就不能在改变,状态不可逆
- 用户可自定义
成功的数据
及失败的原因
- Promise 实例拥有一个
then
方法。接受两个参数,一个成功回调,一个失败回调 - executor执行器在执行过程中,可能会抛出异常 throw new Error(),需要 try catch 捕获
- 当执行器中的是异步代码时(如定时器执行resolve),不是立即执行 状态不会变更,此时then方法中状态仍然是
pending
- 一个 promise 实例可以被 then 多次,分别创建一个
队列用来存放成功和失败回调事件
- 定时器执行时,判断状态为 pending,非立即执行,而是先
存放
成功/失败 回调事件 - 等待状态变为成功或失败时
遍历队列依次执行
对应的onfulfilled
和onrejected
,并且有执行顺序 - Promise 实现链式调用,返回的并不是this,而是一个
新的promise实例
, 因为原有的promise状态一旦发生改变,就不能再改变,否则不符合规范 - Promise 成功和失败的回调的返回值,可以传递给下一次的的then
- 13.1.
返回的是普通值
:不论then是成功还是失败。只要是普通值就传递到下一次then的成功中。(普通值包括:非错误非promise,包括对象) - 13.2.
报错或异常
:一定会走到下一次then的失败中。如果离自己最近的then没有错误处理,会向下找特别注意
: return new Error()返回的是错误对象,属于普通值走下一次then的成功,而throw new Error() 是抛出异常,需要try catch 会走下一次的失败
- 13.3.
返回的是promise
:会采用promise的状态,决定下一次then是成功还是失败,此处会有递归
判断 - 13.4. 总结: 如果返回一个普通值,除了promise ,就传递给下一个then的成功,如果返回一个失败的promise或者抛出异常,会走下一个then的失败
Promise 值的穿透
,当一个promise连续then 多次,并且then中没有返回任何值时,此时data会穿透至最后一个then中- 14.1 执行
onfulfilled
时,先判断是不是一个function,如果是就直接执行,反之就包一层函数
- 14.2 执行
onrejected
时,先判断是不是一个function,如果是就直接执行,反之就抛出失败异常
- 14.1 执行
- 既然promise是为了解决异步回调函数嵌套问题,那么可以通过promise的延迟对象来减少一层嵌套关系
- promiseA+ 规范测试
- 聊点规范以外的东东吧~~
catch
,finally
,resolve
,reject
,all
,race
,就这么多
myPromise的实现
下面就来实现以上要点
myPromise — 实现简单的同步
myPromise.js
// 要点 3: 有三种状态, 相当于是常量,可以放在外面
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
// 要点 1: Promise 就是一个类
class Promise {
// 要点 2: 接收一个函数executor作为参数,立即执行
constructor(executor) {
this.status = PENDING
this.value = undefined // 要点 5: 用户可自定义 成功的数据
this.reason = undefined // 要点 5: 用户可自定义 失败的原因
let resolve = (value) => {
// 要点 4: 状态不可逆, 只有PENDING时,才可以改变状态
if (this.status === PENDING) {
this.value = value // 成功的数据
this.status = RESOLVED // 状态置为 RESOLVED
}
}
let reject = (reason) => {
// 要点 4: 状态不可逆, 只有PENDING时,才可以改变状态
if (this.status === PENDING) {
this.reason = reason // 失败的原因
this.status = REJECTED // 状态置为 REJECTED
}
}
// 要点 7: 错误处理,抛出异常 throw new Error()
try {
executor(resolve, reject) // 立即执行
} catch(e) { // 抛出异常 直接失败
reject(e)
}
}
// 要点 6: 拥有一个then方法(实例上的方法)。接受两个参数回调函数
then(onfulfilled, onrejected) {
if(this.status === RESOLVED){
onfulfilled(this.value)
}
if(this.status === REJECTED){
onrejected(this.reason)
}
}
}
// Commonjs规范导出模块
module.exports = Promise
test.js:简单的同步操作验证
// 使用自己的promise,注释该行代码,就是原生promise
let Promise = require('./1.myPromise')
let p = new Promise((resolve, reject) => {
resolve('成功')
// throw new Error('失败了')
// reject('失败')
})
p.then((data) => {
console.log(data)
}, (err) => {
console.log(err)
})
myPromise — 增加异步功能
在实现了简单的同步操作后,再来看看异步代码的执行,还是使用上面 test 中的案例,简单的改造一下,写一个定时器模拟异步环境,
test.js:增加异步功能
let Promise = require('./1.myPromise')
let p = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('成功')
},1000)
})
p.then((data) => {
console.log(data,1)
}, (err) => {
console.log(err)
})
// 一个实例可以then多次,执行结果会是一样的,因为状态已经固定
p.then((data) => {
console.log(data,2)
}, (err) => {
console.log(err)
})
myPromise.js
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
class Promise {
constructor(executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
// 要点 9: 分别创建一个`队列用来存放成功和失败回调事件
this.onfulfilledCallbacks = []
this.onrejectedCallbacks = []
let resolve = (value) => {
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onfulfilledCallbacks.forEach(fn => fn()) // 要点 11: 状态变为成功时遍历队列依次执行
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onrejectedCallbacks.forEach(fn => fn()) // 要点 11: 状态变为失败时遍历队列依次执行
}
}
try {
executor(resolve, reject)
} catch(e) {
reject(e)
}
}
then(onfulfilled, onrejected) {
if(this.status === RESOLVED){
onfulfilled(this.value)
}
if(this.status === REJECTED){
onrejected(this.reason)
}
// console.log(this.status) // PENDING
// 要点 8: 定时器执行resolve时,状态仍然是pending
if(this.status === PENDING){
// 要点 10: 一个 promise 实例可以被 then 多次,存放 成功回调事件
this.onfulfilledCallbacks.push(() => {
onfulfilled(this.value)
})
// 要点 10: 一个 promise 实例可以被 then 多次,存放 失败回调事件
this.onrejectedCallbacks.push(() => {
onrejected(this.reason)
})
}
}
}
// Commonjs规范导出模块
module.exports = Promise
myPromise — 链式调用(重难点,不好理解)
链式调用这一部分,比较绕,不太好理解,我的老师鼓励我,说:“书读百遍,其义自现”,哈哈哈,现在用来鼓励大家吧!!!
test.js
let Promise = require('./1.myPromise')
let p = new Promise((resolve, reject) => {
resolve('成功')
})
// 可以分别注释用例代码,进行效果演示
p.then((data) => {
return data // 用例1. 返回的是普通值: ‘成功’
// throw new Error() // 用例2. 报错或异常: 抛出异常,会直接走下一次then的失败
// return new Error() // 用例3. 返回的是普通值: error对象,需要特别注意
// return new Promise((s,j)=>s(1)) // 用例4. 返回的是promise: 会传递当前promise的已成功结果
// return new Promise((s,j)=>j(1)) // 用例5. 返回的是promise: 会传递当前promise的已失败结果
}).then((data) => {
console.log(data,2) // 执行结果:用例1, 用例3, 用例4
}, (err) => {
console.log(err,3) // 执行结果:用例2, 用例5
})
// 1. 返回的是普通值:不论then是成功还是失败。只要是普通值就传递到下一次then的成功中。(普通值包括:非错误非promise,包括对象)
// 2. 报错或异常:一定会走到下一次then的失败中。如果离自己最近的then没有错误处理,会向下找
// 3. 特别注意: return new Error()返回的是错误对象,属于普通值走下一次then的成功,而throw new Error() 是抛出异常,会走下一次的失败
// 4. 返回的是promise:会采用promise的状态,决定下一次then是成功还是失败,此处会有递归判断
// 5. 总结: 如果返回一个普通值,除了promise ,就传递给下一个then的成功,如果返回一个失败的promise或者抛出异常(try catch),会走下一个then的失败
myPromise.js
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) { // 防止自己等待自己 一直循环等待
// 原生返回:return reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
return reject(new TypeError('我们自己报的错:循环引用报错'))
}
let called; // 为了兼容其他promise 库符合a+规范,防止状态改变后再次被调用
// 考虑其他promise库兼容问题:判断x是不是promise: 不是null的对象 || 函数
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try { // 为防止then时 出现异常,Object.defineProperty
let then = x.then // 取x 的then 方法
/**
* 可能对象是{then:{}},还需要判断then是否是函数,
* 是函数就认为是promise,并调用then方法
*/
if (typeof then === 'function') {
// call第一个参数是this,后面的是成功回调 和 失败回调
// 如果成功回调返回值 y 仍然是promise,就继续递归解析promise,解析到返回值不是一个Promise类型为止
// promise2能否成功,是根据x的值来定的,x是promise,那么就要等到x完成,x完成后,如果返回y又是promise,那promise2又要等到y完成
then.call(x, y =>{
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, e => {
if (called) return
called = true
reject(e)
})
} else {
// 否则就认为 then 是一个普通对象,成功即可
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e) // 出现异常,直接走失败逻辑
}
} else { // x 为普通值
resolve(x)
}
}
class Promise {
constructor(executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onfulfilledCallbacks = []
this.onrejectedCallbacks = []
let resolve = (value) => {
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onfulfilledCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onrejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onfulfilled, onrejected) {
// 要点 12: then链式调用,返回的并不是this,而是一个`新的promise实例`
let promise2 = new Promise((resolve, reject) => { // new Promise执行器中的代码是立即执行,因此没有影响
if (this.status === RESOLVED) {
// 加定时器是因为在当前执行上下文中,不能获取promise2的值
setTimeout(() => {
// 要点13.2. 用于报错或抛出异常处理,以下同理
try {
// 要点13:存储第一次then的回调结果返回值,用于下次then的参数传递,以下let x 同理
let x = onfulfilled(this.value)
// 要点13.3: x 可能会是一个promise, 需要单独处理,
// 并将promise2、新的返回值:x 、成功回调resolve 失败回调reject 作为参数传递
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
// 要点13.2. 报错或抛出异常处理,直接走promise2的reject,以下同理
reject(e)
}
})
}
// 以下逻辑同理
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.status === PENDING) {
// 暂存成功回调队列
this.onfulfilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
// 暂存失败回调队列
this.onrejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return promise2 // 要点 12: 返回一个新的promise实例
}
}
// Commonjs规范导出模块
module.exports = Promise
myPromise — 值的透传
Promise 值的穿透,当一个promise连续then多次 ,并且then中没有返回任何值时,此时data会穿透值最后一个then中
test.js
//原生方法:值的穿透 演示
let p = new Promise((resolve,reject)=>{
resolve(1)
// reject(2) // 同理
})
p.then().then().then(data=>{
console.log(data); // 1
})
p.then().then().then(null,err=>{
console.log(err); // 2
})
// ----------------------------------------------------------
//myPromise:基于以上几部分的实现,我们自己的promise是不支持穿透的,那么看一下演变过程,就很容易写出符合规范的透传
const Promise = require('./1.myPromise')
let p1 = new Promise((resolve,reject)=>{
resolve(1)
})
let p2 = new Promise((resolve,reject)=>{
reject(2) // 同理,
})
// 相当于, 啥都不写时,感觉像是默认定义了一个函数,不停向下传递,去实现一下吧~
p1.then(data => data)
.then(data => data)
.then(data =>{
console.log(data);
})
// 相当于, throw err,不停向下传递
p2.then(null,err=>{
throw err
}).then(null,err=>{
console.log(err);
throw err
}).then(null,err=>{
console.log(err);
})
myPromise.js
上面重复的代码是在是太多了,就不在赘述了,怕看着更晕了,来, 找到类中then方法定义的位置,加上下面两行代码,就可以完美实现成功及失败的数据透传
...
...
...
// 在这里这里...其他都不变
then(onfulfilled, onrejected) {
// onfulfilled 是函数就执行,不是函数包一层函数并直接返回数据
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : v => v;
// onrejected 是函数就执行,不是函数包一层函数并直接抛出错误
onrejected = typeof onrejected === 'function' ? onrejected : err => { throw err };
...
...
...
完美通过官方872条用例
为了测试myPromise库,必须公开一个非常小的适配器接口。可以说是promise的延迟对象defer,下文会详细介绍,各位稍安勿躁。
- 第一步:找到文件最下面。加上下面几行代码,
/**
* ...
* 其余不变
* ...
*/
// 测试自己的写的promise 是否符合a+规范
// promise的延迟对象
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject
})
return dfd;
}
module.exports = Promise
- 第二步:安装测试包
npm install promises-aplus-tests -g
- 第三步:进入对应目录执行
promises-aplus-tests ./1.myPromise.js
- 如果你的测试结果和我图中标注一样,那么恭喜你,如果验证不通过,肯定是有一些不一样,注意看报错信息,修正一下就好了
myPromise的延迟对象defer用法
promise是为了解决异步回调函数嵌套问题,那么可以通过promise的延迟对象来减少一层嵌套关系
模拟数据准备,准备以下两个文件,
- name.txt:文件内容是:age.txt
- age.txt:文件内容18
需求:首先通过fs
模块读取到name中的内容(读到新的filepath),再去读取age.txt中的内容并输出
没有延迟对象的写法:
const fs = require('fs')
const Promise = require('./1.myPromise')
function read(filepath){
// 想要read方法可以使用then方法,需要包一层new Promise
return new Promise((resolve,reject)=>{
fs.readFile(filepath,'utf8',function(err,data){
if(err) return reject(err)
resolve(data)
})
})
}
read('./name.txt').then(data=>{
return read(data) // data = age.txt
}).then(data=>{
console.log(data); // data = age.txt
})
使用延迟对象的写法:
const fs = require('fs')
const Promise = require('./1.myPromise')
function readDefer(filepath){
let dfd = Promise.defer() // 减少一层嵌套
fs.readFile(filepath,'utf8',function(err,data){
if(err) return dfd.reject(err)
dfd.resolve(data)
})
return dfd.promise
}
readDefer('./name.txt').then(data=>{
return readDefer(data) // data = age.txt
}).then(data=>{
console.log(data); // data = age.txt
})
myPromise.catch
完成以上内容,就已经实现了Promise A+ 规范的全部内容,而我们常用的catch
及finally
并不属于 A+规范,同样可以实现一下,catch
是属于实例上的方法
,用于出错处理及捕获异常
演变过程:test.js
const Promise = require('./1.myPromise')
new Promise((resolve, reject) => {
reject('失败')
}).then(data => { // 第一个then没有错误处理方法,直接会传递到第二个then中
console.log(data);
}).then(null, err => {
console.log(err, 'errrrrr'); // 失败 errrrrr
})
// 将上述代码中null优化一下,于是就有了catch方法
new Promise((resolve, reject) => {
reject('失败')
}).then(data => {
console.log(data);
}).catch(err => {
console.log(err, 'errrrrr'); // 失败 errrrrr
})
实现:myPromise.js
class Promise{
// 重复代码省略
// constructor(){...}
// then(){...}
catch(onrejected){ // 实例的catch
return this.then(null,onrejected)
}
}
/**
* 其余重复代码省略
*/
myPromise.finally
finally
方法是属于实例上的方法
.表示无论成功还是失败,都会执行,并且可以向下执行。理解起来有点点绕哈,建议多看几遍吧,
test.js
const Promise = require('./1.myPromise')
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('成功') // 1
reject('失败') // 2
}, 1000)
}).finally(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve(1000) // 3 不会采用promise成功的结果,但是会等待执行
reject(9000) // 4 但是返回一个失败的promise,会走catch,相当于throw new Error
}, 1000)
})
}).then(data => {
console.log(data); // 对应上述不同序号组合,输出结果有所不同,可自行组合实验,下方也会给大家作出总结
}).catch(e => {
console.log(e, 'catch');
})
/**
* 整理流程:
* 组合:
* 1+3:成功 + 成功 ,走then=>data 返回 1 返回的数据('成功')
* 1+4:成功 + 失败 ,走then=>err 抛出 4 返回的数据(9000)
* 2+3:失败 + 成功 ,走then=>err 抛出 2 返回的数据('失败')
* 2+4:失败 + 失败 ,走then=>err 抛出 4 返回的数据(9000)
*/
实现:myPromise.js
/**
* 1. finally 传递了一个方法 callback
* 2. finally 方法相当于一个then,特点就是在函数中返回一个promise,并会等待这个promise的完成
* 3. Promise.resolve 又会等待 callback执行完成
*/
class Promise{
// 重复代码省略
// constructor(){...}
// then(){...}
// catch(){...}
finally(cb){
return this.then((data)=>{ // 成功 || 失败 都会执行
// Promise.resolve 目的是等待cb() 后的promise完成
// 成功时调用finally,直接传递this实例成功的数据data,finally方法本身是没有参数的
return Promise.resolve(cb()).then(()=>data)
},(err)=>{
// 失败时调用finally,等待Promise.resolve(cb())的执行的结果,当cb结果为失败时,直接以失败的结果直接走catch
// 而只有在Promise.resolve成功时候,才会去执行.then(()=>{throw err}),抛出this实例reject的err信息
return Promise.resolve(cb()).then(()=>{throw err})
})
}
}
/**
* 其余重复代码省略
*/
myPromise.resolve
Promise.resolve()
方法会创造一个成功的promsie,属于类上的方法,通过className调用
演变过程:test.js
const Promise = require('./1.myPromise')
Promise.resolve(200).then(data => {
console.log(data); // 200
})
// 等价于
new Promise((resolve, reject) => {
resolve(200)
}).then(data => {
console.log(data); // 200
})
实现:myPromise.js
constructor(executor) {
/**
* 其余重复无改动
*/
let resolve = (value) => {
// 一个promise直接resolve 另一个promise时,会等待里面的promise执行完,返回成功的执行结果
if (value instanceof Promise) {
return value.then(resolve, reject)
}
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onfulfilledCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
// reject 一个promise时,不需要单独处理,失败就直接走失败的逻辑,不用处理其返回值
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onrejectedCallbacks.forEach(fn => fn())
}
}
/**
* 其余重复无改动
*/
}
static resolve(value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
myPromise.reject
Promise.reject()
方法会创造一个失败的promsie,属于类上的方法,通过className调用
演变过程:test.js
const Promise = require('./1.myPromise')
Promise.reject(100).then(null,err => {
console.log(err, 'errrrrr'); // 100 errrrrr
})
// 等价于
new Promise((resolve, reject) => {
reject(100)
}).then(data=>{
console.log(data);
},err => {
console.log(err, 'errrrrr'); // 100 errrrrr
})
实现:myPromise.js
class Promise{
/**
* 其余重复无改动
*/
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
}
/**
* 其余重复无改动
*/
myPromise.all
Promise.all()
方法用于将多个 Promise 实例,包装并返回一个新的 Promise 实例p
p
的状态由传递的多个 Promise 决定,分成两种情况。(官网是这样说的)
(1)只有所有promise实例的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时数组中promise的返回值组成一个数组,传递给p
的回调函数。
(2)只要数组的中promise有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
test.js
const Promise = require('./1.myPromise')
Promise.all([
1, 2, 3,
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 1000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功') // 情况1
// reject('失败') // 情况2
}, 1000)})
]).then(data => {
console.log(data); // 情况1. [ 1, 2, 3, '成功', '成功' ]
}).catch(err => {
console.log(err); // 情况2. 失败
})
myPromise.js
class Promise{
// 在原有代码基础上,添加一个静态方法
static all(promises){
return new Promise((resolve,reject)=>{
let result = []
let times = 0
const processSuccess = (index,val)=>{
result[index] = val
if(++times === promises.length){
resolve(result)
}
}
for (let i = 0; i < promises.length; i++) { // 并发。多个请求一起执行
let p = promises[i];
if(p && typeof p.then === 'function'){ // 判断p是不是promise实例,是则继续等待p的执行结果,处理p对应的成功和失败
p.then(data=>{
processSuccess(i,data)
},reject) // 如果其中一个promise失败了,直接执行失败回调,由于reject本身就是函数,故可简写
}else{ // 如果不是promise实例就直接调用成功,并存储该值
processSuccess(i,p)
}
}
})
}
}
myPromise.race
Promise.race()
方法同样是将多个 Promise 实例,包装并返回一个新的 Promise 实例p
。
只要有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
(简单理解:race 赛跑,采用最快的那一个,race方法如果其中一个完成了,其他方法还是会执行完,只是并没有采用他的结果)
test.js
const Promise = require('./1.myPromise')
Promise.race([
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 2000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
reject('失败')
}, 1000)
})
]).then(data => {
console.log(data);
}).catch(err => {
console.log(err); // 输出:失败,resolve 2s执行,reject 1s执行,reject更快
})
myPromise.js
class Promise{
static race(promises){
return new Promise((resolve,reject)=>{
// for循环执行,只要有一个状态发生改变,就直接将该实例的返回值 返回
for (let i = 0; i < promises.length; i++) {
let p = promises[i];
if(p && typeof p.then === 'function'){
p.then(data=>{
resolve(data)
},reject)
}else{
resolve(p)
}
}
})
}
}
myPromise终极版本
😂😂在我耐心终将耗尽之际,终于迎来了终极版
show一下myPromise全家桶
,这里终极版本,为了大家看着方便,我就把注释全都取掉了,有不理解的可以返回去对应的标题查看解说,来咯
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) {
return reject(new TypeError('循环引用报错'))
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
let called;
try {
let then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, e => {
if (called) return
called = true
reject(e)
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
class Promise {
constructor(executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onfulfilledCallbacks = []
this.onrejectedCallbacks = []
let resolve = (value) => {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onfulfilledCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onrejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : v => v;
onrejected = typeof onrejected === 'function' ? onrejected : err => {
throw err
};
let promise2 = new Promise((resolve, reject) => {
if (this.status === RESOLVED) {
setTimeout(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.status === PENDING) {
this.onfulfilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
this.onrejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return promise2
}
catch (onrejected) {
return this.then(null, onrejected)
}
finally(cb) {
return this.then(
(data) => {
return Promise.resolve(cb()).then(() => data)
}, (err) => {
return Promise.resolve(cb()).then(() => { throw err })
})
}
static resolve(value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
static all(promises) {
return new Promise((resolve, reject) => {
let result = []
let times = 0
const processSuccess = (index, val) => {
result[index] = val
if (++times === promises.length) {
resolve(result)
}
}
for (let i = 0; i < promises.length; i++) {
let p = promises[i];
if (p && typeof p.then === 'function') {
p.then(data => {
processSuccess(i, data)
}, reject)
} else {
processSuccess(i, p)
}
}
})
}
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
let p = promises[i];
if (p && typeof p.then === 'function') {
p.then(data => {
resolve(data)
}, reject)
} else {
resolve(p)
}
}
})
}
}
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject
})
return dfd;
}
module.exports = Promise
🧡🧡🧡Click Me🧡🧡🧡
既然你都看到这了,想必你肯定是感兴趣啦,希望这篇文章对你更加了解Promise有所帮助,后续工作之余还会分享更多实用的技巧和源码分析,点个赞加个关注再走呗