Promise系列文章
上一篇文章中,我们根据 Promise A+协议已经简单地实现了 Promise,并且测试通过,本次我们来补全 Promise 的一些常用的API.首先我们将API先简单地分为类得静态方法和原型方法,其中静态的包括:resolve、reject、all、race、allSettled,原型方法:catch、finally。
静态方法
Promise.resolve(value)
方法规定:
- 方法返回一个给定值解析后的 Promise 对象。
- 如果 value 是一个非 Promise 对象,则直接作为参数返回
- 如果 value 是一个可以 thenable 的 Promise 对象,则返回的是 Promise 展平之后最终状态
static resolve(value) {
return new Promise((resolve, reject) => {
// 如果是非promise对象直接返回
// 如果是Promise对象,我们需要在构造函数的resolve的方法做一些处理,将此promise对象继续then,并且将我们的resolve,reject作为then的两个回调参数传入,如果后续then完成回调,调用了resolve/reject,也通知了外层的Promise成功还是失败
resolve(value)
})
}
Promise.reject(reason)
方法规定: 返回一个带拒绝原因的 Promise 对象
static reject(reason) {
return new Promise((resolve,reject) => {
reject(reason)
})
}
Promise.all(promise)
方法规定:
- 接受一个可遍历的数组,这个数组可以是 Promise 对象也可以是非 Promise 对象
- 返回一个 Promise 实例
- Promise 的 resolve 是在所有成功回调结束,按顺序将非解构/解构的值当如数组,数组作为 resolve 的 value 传入
- 当 Promise 解构一个失败的时候,将失败的原因直接 reject 发起调用
static all(promises) {
// 判断promise是否可以迭代,如果不是的话抛出异常
...
return Promise((resolve,reject) => {
// 暂存所有回调或者非结构值,最后统一resolve
const results = []
let times = 0
function resloveData(data, index) {
// 通过index来保值返回值和传入的顺序一样
results[index] = data
times++
if(times === promises.length) {
resolve(results)
}
}
for(let index = 0; index < promises.length; index++) {
const _p = promises[index]
if(isPromise(_p)) {
// 如果是Promise实例,就then
_p.then(value = {
resloveData(value,index)
}, reason => {
// 如果失败的话,就直接改Promise状态为失败
reject(reason)
})
} else {
// 如果不是Promise实例,就直接加入返回的数组
resloveData(_p,index)
}
}
})
}
Promise.race(promise)
方法定义:
- 接受一个可遍历的数组,这个数组可以是 Promise 对象也可以是非 Promise 对象
- 返回一个 Promise 实例
- Promise 的 resolve 是在第一个回调结束,将第一个回调值作为成功或者失败值返回
- 赛跑,只要一个值拿到了,无论是成功还是失败,或者非解构值,都直接返回
static race(promises) {
return new Promise((resolve, reject) => {
for (let index = 0; index < promises.length; index++) {
const _p = promises[index]
if (isPromise(_p)) {
_p.then(
(value) => {
resolve(value)
},
(reason) => {
reject(reason)
}
)
} else {
resolve(_p)
}
}
})
}
Promise.allSettled(promise)
方法定义:
- 接受一个可遍历的数组,这个数组可以是 Promise 对象也可以是非 Promise 对象
- 返回一个 Promise 实例
- Promise 的 resolve 是在所有回调结束,按顺序将非解构/解构的值,以及装填当如数组,数组作为 resolve 的 value 传入
- 无论成功还是失败都会在成功中返回
static allSettled(promises) {
return new Promise((resolve, reject) => {
const results = []
let times = 0
function setData(index, value, reason, mStatus) {
results[index] = {
status: mStatus,
value: value,
reason: reason,
}
times++
if (times === promises.length) {
resolve(results)
}
}
for (let index = 0; index < promises.length; index++) {
const _p = promises[index]
if (isPromise(_p)) {
_p.then(
(data) => {
setData(index, data, undefined, 'fulfilled')
},
(reason) => {
setData(index, undefined, reason, 'rejected')
}
)
} else {
setData(index, _p, undefined, 'fulfilled')
}
}
})
}
}
原型链方法
原型上面我们用得最多得应该就是 catch,finally,但是这边要注意,这边的 catch 和 finally 要和我们普通 trycatch 的功能不同。
Promise.prototype.catch(err=>{})
catch 可以看做没有 onFulfilledCallback 的 then
catch(error) {
return this.then(null,error)
}
Promise.prototype.finally(() => {})
finally 的一些特殊点
- 回调方法没有值
- 回调方法肯定会被执行
- 返回一个 Promise,可以继续链式调用
finally(callback) {
return this.then(data => {
return Promise.resolve(callback()).then(() => data)
}, reason => {
return Promise.resolve(callback()).then(() => {
throw reason
})
})
}
完整代码
// promise 拥有三种状态 等待态,成功态,拒绝态
const STATUS = {
PENDING: 'PENDING',
FULFILLED: 'FULFILLED',
REJECTED: 'REJECTED',
}
class Promise {
constructor(executor) {
// promise默认状态是Pending
this.status = STATUS.PENDING
this.value = undefined // 存储成功值,方便后面链式调用
this.reason = undefined // 存储失败的原因,方便后面链式调用
// 如果在状态未变情况下,调用then的时候,我们需要将then中成功/失败方法存储,等到状态变化方法主动调用
this.onFulfilledCallbacks = [] // then的成功方法集
this.onRejectedCallbacks = [] // then的失败方法集
// 用户在执行函数到正确完成得时候,会调用resolve函数来修改当前实例的状态,resolve函数支持传入一个参数,作为成功的值
const resolve = (value) => {
// 判断传入的值是否是Promise, 如果还是Promise,那么直接返回then的调用,将resolve/reject传入then的成功和失败回调,如果Promise成功/失败,通过回调参数,修改了当前Promise的状态
if (value instanceof Promise) {
return value.then(resolve, reject)
}
// 只有在等待态下才能修改promise状态
if (this.status === STATUS.PENDING) {
// 修改实例的状态,为成功态
this.status = STATUS.FULFILLED
// 接受用户传来的成功值
this.value = value
// 状态变化之后,发起then的成功方法集调用,实现异步操作
this.onFulfilledCallbacks.forEach((fn) => fn())
}
}
// 用户在执行函数得到错误的内容或者抛出异常的时候,会调用reject函数来修改当前实例的状态,reject函数支持传入一个参数,作为失败/拒绝的原因
const reject = (reason) => {
// 只有等待态下才能修改promise
if (this.status === STATUS.PENDING) {
// 修改实例的状态为失败态
this.status = STATUS.REJECTED
// 接受用户传来的失败原因
this.reason = reason
// 状态变化之后,发起then的成功方法集调用,实现异步操作
this.onRejectedCallbacks.forEach((fn) => fn())
}
}
try {
// new Promise() 会传入一个默认执行的函数,并且函数接受两个可执行的方法参数
executor(resolve, reject)
} catch (error) {
// 如果默认执行的函数执行报错,则直接将状态设置为拒绝
reject(error)
}
}
/**
* 1. 每一个Promise的原型上面都有一个then方法,并且then方法会提供两个可执行方法参数
* @param {*} onFulfilled 成功执行函数
* @param {*} onRejected 失败执行函数
* 2. then方法必须返回一个promise,我们通过new一个新的Promise达到效果
* @returns new Promise()
* 3. then在链式调用的时候,后续then的调用规则
* + 如果在调用当前成功/失败的方法时,如果抛出异常,会走入下一次then的失败方法中
* + 获取到成功/失败的返回值x
* + 如果x是对象
* + x是普通对象,直接走入下一个then的成功方法中
* + x是Promise,获取到x的then,继续调用then,知道成功获取到普通对象或者基础数据类型
* + 在promise2的then的成功和失败方法调用promise对应的 resolve/reject的方法
* + 下一次的then的成功还是失败是由x的成功还是失败决定
* + 如果x是基础数据类型,会走入下一次then的成功方法中(无论成功还是失败都会调用then的成功方法)
* 4.then的后续链式调用,只有在本次抛出异常,或回调方法返回的Promise失败的时候才会走入下一次的失败中,否则都是走下次resolve方法
*
*/
then(onFulfilled, onRejected) {
// 如果onFulfilled/onRejected 无法获取到需要将当前的成功/失败值传给下一个then
// new promise((resolve,reject) =>{resolve(1)}).then().then().then(of=> {console.log(of)}) 需要能够将of值传入
onFulfilled =
typeof onFulfilled === 'function' ? onFulfilled : (data) => data
onRejected =
typeof onRejected === 'function'
? onRejected
: (e) => {
throw e
}
// 创建一个新的promise2,并且在方法最后返回,是then达到链式调用,继续then其实是调用新new出来的promise实例
const promise2 = new Promise((resolve, reject) => {
// 当我们调用then方法的时候,通过判断当前Promise状态,调用不同的传参方法
// 当promise的状态为Fulfilled的时候,调用onFulfilled方法,并且将成功值作为参数传入
if (this.status === STATUS.FULFILLED) {
// 此处需要处理下promise2和x的情况,但是promise2是在new之后赋值的,所以需要使用一个异步宏/微来达到能取到promise
setTimeout(() => {
try {
const x = onFulfilled(this.value)
// 我们使用一个外部方法来统一处理此处
resolvePromise(x, promise2, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
}
// 当promise的状态为REJECTED的时候,调用onRejected方法,并且将失败原因作为参数传入
if (this.status === STATUS.REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason)
// 我们使用一个外部方法来统一处理此处
resolvePromise(x, promise2, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
}
// 当promise的状态为PENDING,这个时候属于异步操作
// 通过发布订阅模式,将resolve和reject方法发布,等到后续状态变化完成之后,发起调用
if (this.status === STATUS.PENDING) {
// 使用装饰器模式/切片编程将成功/失败方法包装,方便我们做其他操作,以及获取状态变化后得 value/reason
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value)
// 我们使用一个外部方法来统一处理此处
resolvePromise(x, promise2, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason)
// 我们使用一个外部方法来统一处理此处
resolvePromise(x, promise2, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
return promise2
}
/**
* promise.prototype.catch
* catch可以看做只调用reject
* @param {*} err
* @returns
*/
catch(err) {
return this.then(null, err)
}
/**
* 首先要理解,这个finally不是try/catch的finally
* 传参函数无论如何都会被执行
* 可以返回一个Promise
*/
finally(callback) {
return this.then(
(data) => {
// 让函数执行, 内部会调用Promise.resolve()方法,如果方法是promise需要等待他完成
return Promise.resolve(callback()).then(() => data)
},
(err) => {
return Promise.resolve(callback()).then(() => {
throw err
})
}
)
}
/********* 静态方法 *******/
/**
* Promise.resolve()方法实现
* + 如果resolve传入一个原始值,直接将值resolve
* + 如果resolve传入一个Promise,这时候需要我们Promise支持resolve(new Promise()),即
* constructor(executor){
* ...
* const resovle = function(value) {
* ...
* if (value instanceof Promise) {
* return value.then(resolve, reject)
* }
* ...
* }
* ...
* }
*
* @param {*} value
* @returns
*/
static resolve(value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
/**
* Promise.all 返回的是一个promise
*/
static all(promises) {
return new Promise((resolve, reject) => {
const result = []
let times = 0
function promiseData(index, val) {
result[index] = val
if (++times === promises.length) {
resolve(result)
}
}
for (let index = 0; index < promises.length; index++) {
const _p = promises[index]
// 判断是否是Promise
if (isPromise(_p)) {
_p.then((data) => {
promiseData(index, data)
}, reject)
} else {
promiseData(index, _p)
}
}
})
}
static race(promises) {
return new Promise((resolve, reject) => {
for (let index = 0; index < promises.length; index++) {
const _p = promises[index]
if (isPromise(_p)) {
_p.then(
(value) => {
resolve(value)
},
(reason) => {
reject(reason)
}
)
} else {
resolve(_p)
}
}
})
}
static allSettled(promises) {
return new Promise((resolve, reject) => {
const results = []
let times = 0
function setData(index, value, reason, mStatus) {
results[index] = {
status: mStatus,
value: value,
reason: reason,
}
times++
if (times === promises.length) {
resolve(results)
}
}
for (let index = 0; index < promises.length; index++) {
const _p = promises[index]
if (isPromise(_p)) {
_p.then(
(data) => {
setData(index, data, undefined, 'fulfilled')
},
(reason) => {
setData(index, undefined, reason, 'rejected')
}
)
} else {
setData(index, _p, undefined, 'fulfilled')
}
}
})
}
}
function isPromise(value) {
return value && typeof value.then === 'function'
}
// 统一处理then中返回的Promise(promise2)的后续变化状态的操作
function resolvePromise(x, promise2, resolve, reject) {
// 如果promise2 就是自己传入的
if (x === promise2) {
return reject(new TypeError('重复调用'))
}
// 判断传入的x是否是对象或者方法
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
// 为了兼容其他人的Promise, 防止其他promise重复调用加一个防重
let called
// 如果抛出异常,就reject(x)
try {
// 获取到x的then属性
const then = x.then
// 如果then是function,我们就默认x是Promise
if (typeof then === 'function') {
// 调用x的then方法,如果x内部成功/失败调用对应的成功和失败
then.call(
x,
(y) => {
if (called) return
called = true
// 如果y还是Promise 需要再次解析then,知道不是Promise
// resolve(y)
resolvePromise(y, promise2, resolve, reject)
},
(r) => {
if (called) return
called = true
reject(r)
}
)
} else {
// 如果then不是函数,我们默认这个x是一个非Promise对象,直接resolve(x)
resolve(x)
}
} catch (error) {
if (called) return
called = true
reject(error)
}
} else {
// 如果传入的值是一个原始值类型,直接resolve(x)
resolve(x)
}
}
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise