手动实现 Promise
Promise/A+
Promises/A+标准是一个开放、健全且通用的 JavaScript Promise 标准,符合该标准,我们即可提供给他人使用
基本使用
new Promise((resolve, reject) => {
const random = Math.random()
if (random > 0.5) {
resolve('success')
} else {
reject('failure')
}
}).then(value => {
console.log('value', value)
}, e => {
console.log('e', e)
}).catch(error => {
console.log('catch error', error)
})
核心方法
- Promise 有三种状态,进行中 pending,已完成 fulfilled,已失败 rejected
- Promise 是一个构造函数,实例化时传入一个函数作为处理器,处理器立即执行
- 处理器函数有两个参数 resolve 和 reject 分别将结果转变为成功和失败
- Promise 对象执行的成功结果由 resolve 传递,失败原因由 reject 传递
- Promise 的原型上定义着 then 方法,then 方法接受两个参数,分别在成功和失败时执行
由以上写出基本结构
class PromiseA {
constructor(executor) {
// 状态
this.state = 'pending'
// 成功值
this.value = undefined
// 失败原因
this.reason = undefined
const resolve = () => {}
const reject = () => {}
try {
// 处理器立即执行
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {}
}
resolve 和 reject 回调实现
Promise/A+规定,由 pending 改变为 fulfilled 或rejected后不可再次更改,因此在更新状态时需判断,如果是等待态才可更新
const resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
}
}
const reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
}
}
实现 then 方法
then 的参数非必填,Promises/A+规范中定义非函数类型可忽略
class PromiseA {
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
if (typeof onFulfilled === 'function') {
onFulfilled(this.value)
}
}
if (this.state === 'rejected') {
if (typeof onRejected === 'function') {
onRejected(this.reason)
}
}
}
}
实现异步
const p1 = new PromiseA((resolve, reject) => {
setTimeout(() => {
resolve(1);
},1000);
})
p.then(data => console.log(data)) // 无输出
then 方法在执行时状态仍是pending,故接下来在 then 中添加,在等待态时将方法寄存在队列,当状态发生改变时再执行
class PromiseA {
constructor() {
// ...
// 保存成功回调
this.onFulfilledCallbacks = []
// 保存失败回调
this.onRejectedCallbacks = []
// ...
}
}
修改 then 方法
class PromiseA {
then(onFulfilled, onRejected) {
// 等待态,此时异步代码还未走完,将回调放入队列
if (this.state === 'pending') {
if (typeof onFulfilled === 'function') {
this.onFulfilledCallbacks.push(() => {
onFulfilled(this.value)
})
}
if (typeof onRejected === 'function') {
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
}
}
// ...
}
}
在状态改变时执行队列中的函数
class PromiseA {
constructor(executor) {
const resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
this.onFulfilledCallbacks.forEach(cb => cb())
}
}
const reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
this.onRejectedCallbacks.forEach(cb => cb())
}
}
}
}
实现链式调用
PromiseA+对 then 的规范:
- 首先
then方法必须返回一个Promise对象(划重点) - 如果
then方法中返回的是一个普通值(如Number、String等)就使用此值包装成一个新的Promise对象返回 - 如果
then方法中出现异常,则调用失败态方法(reject)跳转到下一个then的 onRejected - 如果
then方法没有传入任何回调,则继续向下传递(值穿透) - 如果
then方法中返回了一个Promise对象,那就以这个对象为准,返回它的结果
class PromiseA {
then(onFulfilled, onRejected) {
// 如果 then 方法没有传入任何回调,将其变成普通值返回
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected =
typeof onRejected === 'function'
? onRejected
: error => {
throw error
}
// then 方法必须返回一个 Promise 对象
// 实例化一个Promise,把原来写的代码放到该实例的处理器函数中
const p2 = new PromiseA((resolve, reject) => {
if (this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
// 使用 queueMicrotask 将函数添加到微任务执行队列
queueMicrotask(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
try {
const x = onRejected(this.reason)
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
}
if (this.state === 'fulfilled') {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if (this.state === 'rejected') {
queueMicrotask(() => {
try {
const x = onRejected(this.reason)
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
})
return p2
}
}
/**
* 解析then返回值与新Promise对象
* @param {Object} 新的Promise对象,就是我们创建的p2实例
* @param {x} 上一个then的返回值
* @param {function} resolve p2处理器函数的resolve
* @param {function} reject p2处理器函数的reject
*/
const resolvePromise = (p2, x, resolve, reject) => {
// 解决循环引用报错
if (p2 === x) {
// reject报错
reject(new TypeError('请避免Promise循环引用'))
}
// 对返回值 x 分情况处理
// x 是对象或函数
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 定义状态-防止多次调用
let called = false
try {
const { then } = x
// x 有then方法即可认为是 Promise 对象
if (typeof then === 'function') {
// 执行then 使用call传递this 第一个参数是this 后面是成功的回调 和 失败的回调
then.call(
x,
y => {
// 成功和失败只能调用一个
if (called) return
called = true
// 递归调用,传入y若是Promise对象,继续循环
resolvePromise(p2, y, resolve, reject)
},
error => {
// 成功和失败只能调用一个
if (called) return
called = true
reject(error)
}
)
} else {
resolve(x)
}
} catch {
if (called) return
called = true
reject(x)
}
} else {
// 普通类型的值直接返回
resolve(x)
}
}
其他方法
其他方法如catch,all,race均较为简单,此处附上完整代码
/**
* 定义状态常量
*/
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
/**
* 判断是否为可迭代类型
*/
const isIterable = iterator => {
return (
iterator !== null &&
iterator !== undefined &&
typeof iterator[Symbol.iterator] === 'function'
)
}
/**
* 是否为Map
*/
const isMap = map => {
return toString.call(map) === '[object Map]'
}
/**
* 是否为Set
*/
const isSet = set => {
return toString.call(set) === '[object Set]'
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class PromiseA {
constructor(exec) {
this.state = PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledCallbacks = []
this.onRejectedCallback = []
const resolve = value => {
if (this.state === PENDING) {
this.state = FULFILLED
this.value = value
this.onFulfilledCallbacks.forEach(cb => cb())
}
}
const reject = reason => {
if (this.state === PENDING) {
this.state = REJECTED
this.reason = reason
this.onRejectedCallback.forEach(cb => cb())
}
}
try {
exec(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected =
typeof onRejected === 'function'
? onRejected
: error => {
throw error
}
const p2 = new PromiseA((resolve, reject) => {
if (this.state === PENDING) {
this.onFulfilledCallbacks.push(() => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
this.onRejectedCallback.push(() => {
queueMicrotask(() => {
try {
const x = onRejected(this.reason)
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
}
if (this.state === FULFILLED) {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if (this.state === REJECTED) {
queueMicrotask(() => {
try {
const x = onRejected(this.reason)
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
})
return p2
}
static resolve(value) {
return new PromiseA(resolve => {
resolve(value)
})
}
static reject(reason) {
return new PromiseA((_, reject) => {
reject(reason)
})
}
/**
* 全部完成
*/
static all(promises) {
return new PromiseA((resolve, reject) => {
const results = []
if (!isIterable(promises)) {
throw new TypeError(`${typeof promises} is not iterable`)
}
if (isMap(promises)) {
resolve(Array.from(promises))
}
if (isSet(promises)) {
promises = Array.from(promises)
}
if (!promises.length) {
resolve(results)
}
// 每完成一个promise,count 减 1,为0时返回结果
if (typeof promises === 'string') {
promises = promises.split('')
}
let count = promises.length
promises.forEach((promise, index) => {
if (promise instanceof this) {
promise.then(
value => {
results[index] = value
count -= 1
if (!count) resolve(results)
},
reason => {
reject(reason)
}
)
} else {
results[index] = promise
count -= 1
if (!count) resolve(results)
}
})
})
}
/**
* 等待所有promise结果
*/
static allSettled(promises) {
return new PromiseA(resolve => {
const results = []
if (!isIterable(promises)) {
throw new TypeError(`${typeof promises} is not iterable`)
}
if (isMap(promises) || isSet(promises)) {
promises = Array.from(promises)
}
if (!promises.length) {
resolve(results)
}
if (typeof promises === 'string') {
promises = promises.split('')
}
let count = promises.length
promises.forEach((promise, index) => {
if (promise instanceof this) {
promise.then(
value => {
results[index] = {
status: FULFILLED,
value
}
count -= 1
if (!count) resolve(results)
},
reason => {
results[index] = {
status: REJECTED,
reason
}
count -= 1
if (!count) resolve(results)
}
)
} else {
results[index] = {
status: FULFILLED,
value: promise
}
count -= 1
if (!count) resolve(results)
}
})
})
}
/**
* 任意一个有结果,无论成功与否
*/
static race(promises) {
return new PromiseA((resolve, reject) => {
if (!isIterable(promises)) {
throw new TypeError(`${typeof promises} is not iterable`)
}
if (isMap(promises) || isSet(promises)) {
promises = Array.from(promises)
}
if (typeof promises === 'string') {
promises = promises.split('')
}
promises.forEach(promise => {
if (promise instanceof this) {
promise.then(
value => {
resolve(value)
},
reason => {
reject(reason)
}
)
} else {
resolve(promise)
}
})
})
}
/**
* 任意一个成功即可
*/
static any(promises) {
return new PromiseA((resolve, reject) => {
const rejects = []
if (!isIterable(promises)) {
throw new TypeError(`${typeof promises} is not iterable`)
}
if (isMap(promises) || isSet(promises)) {
promises = Array.from(promises)
}
if (!promises.length) {
reject(new AggregateError('', 'All promises were rejected'))
}
if (typeof promises === 'string') {
promises = promises.split('')
}
let count = promises.length
promises.forEach((promise, index) => {
if (promise instanceof this) {
promise.then(
value => {
resolve(value)
},
reason => {
rejects[index] = reason
count -= 1
if (!count) {
reject(new AggregateError('', 'All promises were rejected'))
}
}
)
} else {
resolve(promise)
}
})
})
}
catch(onRejected) {
return this.then(null, onRejected)
}
/**
* finally 表示不是最终的意思,而是无论如何都会执行的意思
* 如果返回一个 promise 会等待这个 promise 也执行完毕
* 上一次成功且返回 promise 成功,采用上一次的结果
* 上一次失败或返回 promise 失败,采用失败的结果
* 上一次失败且返回 promise 失败,采用返回promise失败的结果
* 如返回的不是promise,则其返回值没有意义
*/
finally(callback) {
return this.then(
value => {
const p1 = callback()
if (p1 instanceof PromiseA) {
return p1.then(
() => {
return value
},
error2 => {
throw error2
}
)
} else {
return value
}
},
error1 => {
const p1 = callback()
if (p1 instanceof PromiseA) {
return p1.then(
() => {
throw error1
},
error2 => {
throw error2
}
)
} else {
throw error1
}
}
)
}
}
/**
* 解析结果
*/
const resolvePromise = (p2, x, resolve, reject) => {
if (p2 === x) {
reject(new TypeError('请避免promise循环引用'))
}
let called = false
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
const { then } = x
if (typeof then === 'function') {
then.call(
x,
y => {
if (called) return
called = true
resolvePromise(p2, y, resolve, reject)
},
error => {
if (called) return
called = true
reject(error)
}
)
} else {
resolve(x)
}
} catch (error) {
if (called) return
called = true
reject(error)
}
} else {
resolve(x)
}
}
代码测试
将以下代码添加到文件中
PromiseA.deferred = function () {
const defer = {}
defer.promise = new PromiseA((resolve, reject) => {
defer.resolve = resolve
defer.reject = reject
})
return defer
}
try {
module.exports = PromiseA
} catch (error) {
console.error(error)
}
安装依赖,执行命令即可,测试通过后即算完成
npm install promises-aplus-tests -D
npx promises-aplus-tests Promise.js