Promise 是解决异步编程回调嵌套过多导致代码可读性下降的方法之一。
一个 promise 实例有三个状态:pending 等待中、fulfulled 已执行、rejected 已拒绝,状态只会从 pending 切换到 fulfulled 或 rejected,且保持不变。
只有调用了 resolve 或 reject 方法,then 函数的两个参数 onFulfilled 、onRejected 才会被执行,所以当调用 then 时需要用数组将 onFulfilled 、onRejected 存起来,待 resolve 执行后再从数组中取出每个回调函数且异步调用:setTimeout(onFulfilled/onRejected)
。
实现链式调用 then 可以通过在 then 函数内部返回一个新的 promise。
then 的 onFulfilled 方法执行完毕后可以返回任意值提供给下一个 then 方法,可能会返回一个 promise 或具有 then 接口的对象,所以需要递归调用,实现一个 resolvePromise 方法完成这些功能。
详细的 Promise 规范要求:https://promisesaplus.com/
/**
* promise 各阶段状态
*/
const State = {
/**
* 等待中
*/
PENDING: 'pending',
/**
* 已执行
*/
FULFILLED: 'fulfilled',
/**
* 已拒绝
*/
REJECTED: 'rejected'
}
class Promise {
constructor(executor) {
if (!isFunction(executor)) throw new TypeError(`Promise resolver ${executor} is not a function`)
/**
* 终值
*/
this.value = null
/**
* 拒绝原因
*/
this.reason = null
/**
* 当前状态,初始为等待中
*/
this.state = State.PENDING
/**
* 已完成状态的 then 待执行回调函数数组
*/
this.onFulfilledCallbacks = []
/**
* 已拒绝状态的 then 待执行回调函数数组
*/
this.onRejectedCallbacks = []
const resolve = value => {
if (this.state === State.PENDING) {
this.state = State.FULFILLED
this.value = value
// 状态为完成后,执行回调数组里的函数
this.onFulfilledCallbacks.forEach(fn => fn())
}
}
const reject = reason => {
if (this.state === State.PENDING) {
this.state = State.REJECTED
this.reason = reason
// 状态为拒绝后,执行回调数组里的函数
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then (onFulfilled, onRejected) {
onFulfilled = isFunction(onFulfilled) ? onFulfilled : onDefaultFulfilled
onRejected = isFunction(onRejected) ? onRejected : onDefaultRejected
// 返回新 promise 实现链式调用 then
const promise2 = new this.constructor((resolve, reject) => {
const onAsyncCallback = (state) => {
return () => {
// 规范规定 onFulfilled、onRejected 需要异步执行
setTimeout(() => {
try {
const x = state === State.FULFILLED ? onFulfilled(this.value) : onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
})
}
}
// 如果状态处于待执行,先将回调函数存入数组
if (this.state === State.PENDING) {
this.onFulfilledCallbacks.push(onAsyncCallback(State.FULFILLED))
this.onRejectedCallbacks.push(onAsyncCallback(State.REJECTED))
} else {
onAsyncCallback(this.state)()
}
})
return promise2
}
catch (fn) {
return this.then(null, fn)
}
}
Promise.resolve = function (value) {
return new Promise(resolve => resolve(value))
}
Promise.reject = function (reason) {
return new Promise((resolve, reject) => reject(reason))
}
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject)
}
})
}
Promise.all = function (promises) {
const result = []
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then((value) => {
result.push(value)
if (result.length === promises.length) {
resolve(result)
}
}, reject)
}
})
}
function isFunction (target) { return typeof target === 'function' }
function isObject (target) { return target !== null && typeof target === 'object' }
function onDefaultFulfilled (value) { return value }
function onDefaultRejected (reason) { throw reason }
/**
* 解决 then 方法返回新 promise 并在链式调用时的一些边界情况
* @param {Promise} promise2 新的 promise
* @param {any} x onFulfilled 或 onRejected 返回的值,值可以是任意类型
* @param {Function} resolve promise2 resolve
* @param {Function} reject promise2 reject
*/
function resolvePromise (promise2, x, resolve, reject) {
if (promise2 === x) {
throw new TypeError(`Chaining cycle detected for promise`)
}
// 防止 resolve 和 reject 同时调用
let called = false
if (isObject(x) || isFunction(x)) {
try {
/**
* x 的 then 方法可能是通过代理获得的:
* Object.defineProperty(x, 'then', {
* get () { // 干一些不为人知的勾当,也可能会抛出错误 }
* })
*
* 所以取值后将它缓存,防止触发第二次 getter,并且用 try 包裹,如果出错则 reject
*/
const then = x.then
if (isFunction(then)) {
// 如果是遵循规范的 then 接口,允许它调用
then.call(x, value => {
if (called) return
called = true
resolvePromise(promise2, value, resolve, reject)
}, (reason) => {
if (called) return
called = true
reject(reason)
})
} else {
resolve(x)
}
}
catch (err) {
if (called) return
called = true
reject(err)
}
} else {
// x 为原始数值
resolve(x)
}
}
// 测试:npx promises-aplus-tests promise.js
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
module.exports = Promise