手写 Promise
基本概念
Promise 本质是一个状态机,且状态只能为以下三种:
- Pending(等待态)
- Fulfilled(执行态)
- Rejected(拒绝态)
状态的变更是单向的,Pending 到下一状态,且状态变更不可逆
js
Pending ==> Fulfilled
Pending ==> Rejected
Promise 的基本过程
- 初始化 Promise 状态为 pending
- 立即执行 Promise 中传入的 fn 函数,将内部函数 resolve 和 reject 作为参数传递给 fn,
- 执行 .then() 注册回调处理数组
- then 方法传入的参数 onFulfilled 和 onRejected 参数不是函数是的处理,与是函数时必须在那一轮事件循环之后的新执行栈中执行
所有方法
手写所有方法
- Promise 的含义
- Promise.prototype.then() ES2015
- Promise.prototype.catch() ES2015
- Promise.resolve() ES2015
- Promise.reject() ES2015
- Promise.all() ES2015
- Promise.race() ES2015
- Promise.prototype.finally() ES2018
- Promise.allSettled() ES2020
- Promise.any() ES2021
- Promise.try() Stage 1
- 详细介绍参见这里
- await.ops ESnext
具体文档相关内容,参看 learn-javascript es6 Promise
几个问题
- finally 后面还可以写 then 调用吗
- then catch finally 中回调函数的参数都从哪里来
- 执行时序什么样,如果中途出错呢
- 什么情况是 Promise 会吃掉错误
- 什么情况下应该使用微任务,参见 queueMicrotask
- cancel Promise
- callbackhell 回调地狱
- [译] 如何取消你的 Promise?
- JS“异步体操” - 实现异步编程的最佳实践
手写实现
参照 Promise A+ 的规范一步一步实现 my-promise 1~8.js
js
// 结构
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor() {}
status = PENDING
value = null
reason = null
onFulfilledCallbacks = []
onRejectedCallbacks = []
then() {}
catch() {}
finally() {}
static all() {}
static race() {}
static resolve() {}
static reject() {}
static allSettled() {}
static any() {}
static try() {}
}
js
// 参考学习:https://juejin.cn/post/6945319439772434469
{
// 1. 传入执行器 executor
class MyPromise1 {
constructor(executor) {
// executor 是一个执行器,进入会立即执行
executor()
}
}
}
{
// 2. executor 传入 resolve 和 reject 方法
class MyPromise2 {
constructor(executor) {
// executor 是一个执行器,进入会立即执行
// 并传入 resolve 和 reject 方法
executor(this.resolve, this.reject)
}
// resolve 和 reject 为什么要用箭头函数?
// 如果直接调用的话,普通函数 this 指向的是 window 或者 undefined
// 用箭头函数就可以让 this 指向当前实例对象
// 更改成功后的状态
resolve = () => {}
// 更改失败后的状态
reject = () => {}
}
}
// 3. 状态与结果的管理
// 定义三个常量表示状态
{
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise3 {
constructor(executor) {
executor(this.resolve, this.reject)
}
// 储存状态的变量,初始值是 pending
status = PENDING
// 成功之后的值
value = null
// 失败的原因
reason = null
resolve = (value) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
}
}
reject = (reason) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
}
}
}
}
// 4. then 的简单实现
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING
value = null
reason = null
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
}
}
reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
}
}
then(onFulfilled, onRejected) {
// 判断状态
if (this.status === FULFILLED) {
// 调用成功回调,并且把值返回
onFulfilled(this.value)
} else if (this.status === REJECTED) {
// 调用失败回调,并且把原因返回
onRejected(this.reason)
} else if (this.status === PENDING) {
// 未处理 pending
console.log('pending')
}
}
}
// 对外暴露 MyPromise 类
// export default MyPromise
// testing
const p1 = new MyPromise((resolve, reject) => {
resolve('success')
reject('err')
})
p1.then(
(value) => {
console.log('p1 resolve', value)
},
(reason) => {
console.log('p1 reject', reason)
}
)
// 当前问题,未处理异步逻辑,
// then 会马上执行,虽然判断了 pending 状态,但没有等待这个状态
// 接下来还需要处理一下 pending 的状态,参见 2.js
const p2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 2000)
})
p2.then(
(value) => {
console.log('p2 resolve', value)
},
(reason) => {
console.log('p2 reject', reason)
}
)
js
// 接 1.js 接下来处理 pending 状态
// 先将 1.js 的实现手写迁移过来
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING
value = null
reason = null
// 存储成功或失败的回调函数
onFulfilledCallback = []
onRejectedCallback = []
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
// 状态变更为成功,调用所有成功的回调
while (this.onFulfilledCallback.length) {
// Array.shift() 取出数组第一个元素,然后()调用,shift 不是纯函数,取出后会改变数组,直到数组为空
// 这里清空了数组,实际并不必要,for 循环也是也可以的
// 思考 如果执行的过程出错了,怎么办
this.onFulfilledCallback.shift()(value)
}
}
}
reject = (value) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
// 状态变更为失败,调用所有失败的回调
while (this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(reason)
}
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilledCallback(this.value)
} else if (this.status === REJECTED) {
onRejectedCallback(this.reason)
} else if (this.status === PENDING) {
// 不知道后续的状态变化,先将回调存储起来,
// 等到状态变更时调用
this.onFulfilledCallback.push(onFulfilled)
this.onRejectedCallback.push(onRejected)
}
}
}
// testing pending
const p1 = new MyPromise((resolve, reject) => {
// resolve('success')
setTimeout(() => {
resolve('success')
reject('fail')
}, 2000)
})
p1.then(
(value) => {
console.log('0')
console.log('resolve', value)
},
(reason) => {
console.log('reject', reason)
},
)
// 回调存储使用数组,支持 then 方法多次调用
p1.then(
(value) => {
console.log('1')
console.log('resolve1', value)
},
(reason) => {
console.log('reject1', reason)
},
)
p1.then(
(value) => {
console.log('2')
console.log('resolve2', value)
},
(reason) => {
console.log('reject2', reason)
},
)
// 此时还不支持 then 的链式调用,参见 3.js
js
// 上接 2.js,下面处理 then 的链式调用
// 先手写 2.js 中已实现的功能
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING
value = null
reason = null
onFulfilledCallback = []
onRejectedCallback = []
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
while (this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(value)
}
}
}
reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
while (this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(reason)
}
}
}
// then 链式调用,就要返回一个 Promise 对象
// then 方法里 return 的值作为下一个 then 方法的参数,如果 return 的是一个 promise,就需要判断这个它的状态
then(onFulfilled, onRejected) {
const promise = new MyPromise((resolve, reject) => {
// 这里的内容在执行器中,会立即执行(此时返回为 pending 状态)
if (this.status === FULFILLED) {
// 将成功回调的结果,传入 resolvePromise 集中处理(这里仅处理了 FULFILLED 状态的逻辑)
const x = onFulfilled(this.value)
resolvePromise(x, resolve, reject)
} else if (this.status === REJECTED) {
onRejected(this.reason)
} else if (this.status === PENDING) {
console.log('pending')
this.onFulfilledCallback.push(onFulfilled)
this.onRejectedCallback.push(onRejected)
}
})
return promise
}
}
function resolvePromise(x, resolve, reject) {
// 判断 x 是不是 MyPromise 实例对象
if (x instanceof MyPromise) {
// 是 Promise, 则执行 x, 调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
// x.then(value => resolve(value), reason => reject(reason)) 简化如下
x.then(resolve, reject)
} else {
// 普通值
resolve(x)
}
}
// testing
const p1 = new MyPromise((resolve, reject) => {
// resolve('success')
// reject('fail')
// TODO: 这里当使用异步时,链式调用变得失效了 (此问题待后续处理)
// 可以先思考下这里为什么会导致链式调用失效
setTimeout(() => {
resolve('success')
reject('fail')
}, 2000)
})
// 链式调用
p1.then((res) => {
console.log('1', res)
return 'then1'
}).then((res) => {
console.log('then2', res)
})
// 目前我们没有做异常处理,接下来在 4.js 中我们异常捕获等情况
// NOTE: 思考,这里异步为什么会导致链式调用失效
// NOTE: 解析在下方,你确定要看,不先自行思考下
// 因为 pending 时,存储的回调不具备改变当前 promise 的能力(没有 resolve,reject 的相关处理),
// 所以此时第一个 then 返回的 promise 一直是 pending 状态(不是链式调用失效了,而是一直 pending 还未调用后面的链上 then 方法)
js
// 续 3.js,这里处理一下错误类型处理,以及异常捕获(执行器异常,then 执行异常)
// 先手写 3.js 流程,后补充错误处理
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
// 处理执行器异常捕获
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
status = PENDING
value = null
reason = null
onFulfilledCallback = []
onRejectedCallback = []
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
while (this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(value)
}
}
}
reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
while (this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(reason)
}
}
}
then(onFulfilled, onRejected) {
const promise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
queueMicrotask(() => {
// 处理 then 执行异常捕获
try {
const x = onFulfilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch (err) {
reject(err)
}
})
} else if (this.status === REJECTED) {
onRejected(this.reason)
} else if (this.status === PENDING) {
this.onFulfilledCallback.push(onFulfilled)
this.onRejectedCallback.push(onRejected)
}
})
return promise
}
}
function resolvePromise(promise, x, resolve, reject) {
if (promise === x) {
return reject(
new TypeError('Chaining cycle detected for promise #<Promise>')
)
}
if (x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
// testing
// const p0 = new MyPromise((resolve, reject) => {
// resolve('resolve')
// reject('reject')
// setTimeout(() => {
// resolve('resolve')
// reject('reject')
// }, 2000)
// })
// 链式调用
// p0.then((res) => {
// console.log('1', res);
// return 'then1'
// }).then((res) => {
// console.log('then2', res)
// })
// p0.then((res) => {
// console.log('2', res);
// return 'then2'
// }).then((res) => {
// console.log('then3', res)
// })
// 处理错误
// 1. 处理 执行器错误捕获
// const p1 = new MyPromise((resolve, reject) => {
// // new Error('执行器错误')
// throw new Error('执行器错误')
// }).then(res => {
// console.log('p1 success', res)
// }, err => {
// console.log('p1 err', err.message)
// })
// 2. 处理 then 方法执行错误捕获
// const p2 = new MyPromise((resolve, reject) => {
// // new Error('执行器错误')
// // throw new Error('执行器错误')
// resolve('resolve')
// reject('reject')
// // 这里异步时,未良好处理链式调用,可先跳过
// })
// p2.then(res => {
// // 第一个 then 方法中的错误要在第二个 then 方法中捕获到
// console.log('p2-1 success', res)
// throw new Error('then 方法执行错误')
// }, err => {
// console.log('p2-1 err', err.message)
// }).then(res => {
// console.log('p2-2 success', res)
// }, err => {
// console.log('p2-2 err', err.message)
// })
// 3. then 链式调用自身,导致循环调用问题
// 如果 then 方法返回的是自己的 Promise 对象,则会发生循环调用,这个时候程序会报错
// 原生 Promise 会报类型错误
// const promise = new Promise((resolve, reject) => {
// resolve('success')
// reject('fail')
// })
// TypeError: Chaining cycle detected for promise #<Promise>
// Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
// const p31 = promise.then(res => {
// return p31
// })
// p31.then(res => {
// console.log(res)
// }).catch(err => {
// // Chaining cycle detected for promise #<Promise>
// console.log(err.message)
// })
// 我们这里报
// ReferenceError: Cannot access 'p2' before initialization
// SyntaxError: Identifier 'promise' has already been declared
const promise = new MyPromise((resolve, reject) => {
resolve('success')
reject('fail')
})
// ReferenceError: Cannot access 'p32' before initialization
// 处理这个错误,需要创建一个异步函数去等待 其 (p32) 完成初始化 (可通过 queueMicrotask 实现)
const p32 = promise.then((res) => {
console.log('2', res)
return p32
})
p32.then(
(res) => {
console.log('res', res)
},
(err) => {
console.log('err', err)
}
)
// 以上三个异常情况处理后,我们 fulfilled 分支下的流程算是处理,接下来还要把 rejected 和 pending 条件分支的逻辑都处理掉
// 参见 5.js
js
// 接 4.js, 4 中我们处理了 fulfilled 状态下的错误流程,下面来处理下
// - 将 rejected 和 pending 状态的也改造下
// - then 中的 onRejected 回调可以不传
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
status = PENDING
value = null
reason = null
onFulfilledCallback = []
onRejectedCallback = []
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
while (this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(value)
}
}
}
reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
while (this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(reason)
}
}
}
then(onFulfilled, onRejected) {
// 参数判断
onFulfilled =
typeof onFulfilled === 'function' ? onFulfilled : (value) => value
onRejected =
typeof onRejected === 'function'
? onRejected
: (reason) => {
throw reason
}
const promise = new MyPromise((resolve, reject) => {
// 提取方法,以复用错误
const onResolve = () => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch (err) {
reject(err)
}
})
}
const onReject = () => {
queueMicrotask(() => {
try {
const x = onRejected(this.reason)
resolvePromise(promise, x, resolve, reject)
} catch (err) {
reject(err)
}
})
}
if (this.status === FULFILLED) {
onResolve()
} else if (this.status === REJECTED) {
onReject()
} else if (this.status === PENDING) {
this.onFulfilledCallback.push(onResolve)
this.onRejectedCallback.push(onReject)
}
})
return promise
}
}
function resolvePromise(p, x, resolve, reject) {
if (p === x) {
return reject(
new TypeError('Chaining cycle detected for promise #<Promise>'),
)
}
if (x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
// testing
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('resolve')
reject('reject')
}, 2000)
})
const p1 = promise.then((res) => {
console.log('1', res)
return 'p1'
})
p1.then((res) => {
console.log('2', res)
return '2'
}).then((res) => {
console.log('3', res)
})
js
// 续 5.js, 到这里核心的 Promise 功能就完成了,
// 但还缺少静态方法 resolve、reject
// 这里添加静态方法 Promise.resolve, Promise.reject
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
status = PENDING
value = null
reason = null
onFulfilledCallback = []
onRejectedCallback = []
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
while (this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(value)
}
}
}
reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
while (this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(reason)
}
}
}
then(onFulfilled, onRejected) {
// 解决 onFufilled,onRejected 没有传值的问题
const onFulfilled1 =
typeof onFulfilled === 'function' ? onFulfilled : (value) => value
// 因为错误的值要让后面访问到,所以这里也要抛出错误,不然会在之后 then 的 resolve 中捕获
const onRejected1 =
typeof onRejected === 'function'
? onRejected
: (reason) => {
throw reason
}
const promise2 = new MyPromise((resolve, reject) => {
const onResolve = () => {
queueMicrotask(() => {
try {
const x = onFulfilled1(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
})
}
const onReject = () => {
queueMicrotask(() => {
try {
const x = onRejected1(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
})
}
if (this.status === FULFILLED) {
onResolve()
} else if (this.status === REJECTED) {
onReject()
} else if (this.status === PENDING) {
this.onFulfilledCallback.push(onResolve)
this.onRejectedCallback.push(onReject)
}
})
return promise2
}
// 静态方法 resolve
static resolve(parameter) {
// 如果传入 promise 直接返回
if (parameter instanceof MyPromise) {
return parameter
}
// 转为 promise
return new MyPromise((resolve) => {
resolve(parameter)
})
}
// 静态方法 reject
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
}
function resolvePromise(p, x, resolve, reject) {
if (p === x) {
return reject(
new TypeError('Chaining cycle detected for promise #<Promise>')
)
}
// 此处不符合 2.3.3 规范
if (x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
// 基本完成了
// testing
// const p1 = new MyPromise((resolve, reject) => {
// setTimeout(() => {
// resolve('resolve')
// }, 2000)
// })
// const p2 = new MyPromise((resolve, reject) => {
// setTimeout(() => {
// reject('reject')
// }, 2000)
// })
// MyPromise.resolve().then(res => {
// console.log(res)
// })
// MyPromise.resolve(p1).then(res => {
// console.log('res', res)
// }, err => {
// console.log('err', err)
// })
// MyPromise.resolve(p2).then(res => {
// console.log('res', res)
// }, err => {
// console.log('err', err)
// })
// MyPromise.reject('static reject').then(null, err => {
// console.log(err);
// })
// 接下来我们就可以跑下 Promise A+ 单元测试了
// npm i promises-aplus-tests -D
// promises-aplus-tests MyPromise
// https://promisesaplus.com/
// 单测需要附加以下代码
MyPromise.deferred = function () {
var result = {}
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve
result.reject = reject
})
return result
}
module.exports = MyPromise
/*
6.js 单测从 2.3.3 用例开始报错,这里对比 Promise A+ 规范
```bash
2.3.3: Otherwise, if `x` is an object or function,
2.3.3.1: Let `then` be `x.then`
`x` is an object with null prototype
1) via return from a fulfilled promise
2) via return from a rejected promise
`x` is an object with normal Object.prototype
3) via return from a fulfilled promise
4) via return from a rejected promise
`x` is a function
5) via return from a fulfilled promise
6) via return from a rejected promise
2.3.3.2: If retrieving the property `x.then` results in a thrown exception `e`, reject `promise` with `e` as the reason.
`e` is `undefined`
7) via return from a fulfilled promise
8) via return from a rejected promise
`e` is `null`
9) via return from a fulfilled promise
10) via return from a rejected promise
```
规划 2.3.3 中,要求判断 x.then 是否存在,与前面写的 `x instanceof MyPromise` 类似,但更严格,接下来对这块进行改造
根据这个规范要求,改造一下 resolvePromise 方法
*/
js
// 续 6.js, 处理 resolvePromise 方法中对于规范 2.3.3 ~ 2.3.4 的实施
// https://promisesaplus.com/
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
status = PENDING
value = null
reason = null
onFulfilledCallback = []
onRejectedCallback = []
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
while (this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(value)
}
}
}
reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
while (this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(reason)
}
}
}
then(onFulfilled, onRejected) {
const onFulfilled1 =
typeof onFulfilled === 'function' ? onFulfilled : (value) => value
const onRejected1 =
typeof onRejected === 'function'
? onRejected
: (reason) => {
throw reason
}
const promise2 = new MyPromise((resolve, reject) => {
const onResolve = () => {
queueMicrotask(() => {
try {
const x = onFulfilled1(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
})
}
const onReject = () => {
queueMicrotask(() => {
try {
const x = onRejected1(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
})
}
if (this.status === FULFILLED) {
onResolve()
} else if (this.status === REJECTED) {
onReject()
} else if (this.status === PENDING) {
this.onFulfilledCallback.push(onResolve)
this.onRejectedCallback.push(onReject)
}
})
return promise2
}
static resolve(parameter) {
if (parameter instanceof MyPromise) {
return parameter
}
return new MyPromise((resolve) => {
resolve(parameter)
})
}
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
}
function resolvePromise(p, x, resolve, reject) {
if (p === x) {
return reject(
new TypeError('Chaining cycle detected for promise #<Promise>')
)
}
// 按规范 2.3.3 ~ 2.3.4 对以下逻辑进行处理
// if (x instanceof MyPromise) {
// x.then(resolve, reject)
// } else {
// resolve(x)
// }
// 修改如下
if (typeof x === 'object' || typeof x === 'function') {
if (x === null) return resolve(x)
let then
try {
then = x.then
} catch (err) {
// 如果取 x.then 的值时抛出错误 err,则以 err 为据因拒绝 promise
return reject(err)
}
// 如果 then 是函数 (应该作为函数,这也是 thenable 的条件)
if (typeof then === 'function') {
let called = false
try {
// then 只能被调用一次,状态变更后不可再调用
then.call(
x,
(y) => {
if (called) return
called = true
resolvePromise(p, y, resolve, reject)
},
(r) => {
if (called) return
called = true
reject(r)
}
)
} catch (err) {
if (called) return
reject(err)
}
} else {
// 如果 then 不是函数,则以 x 为参数执行 premise
resolve(x)
}
} else {
// 如果 then 不是对象或函数,则以 x 为参数执行 premise
resolve(x)
}
}
// 单测需要附加以下代码
MyPromise.deferred = function () {
var result = {}
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve
result.reject = reject
})
return result
}
module.exports = MyPromise
// 完成后,可以通过整个 Promise A+ 的单元测试了
js
// 现在 7.js 所有单测都通过了,但是还缺少一些方法
// catch
// finally ES2018
// all
// race
// allSettled ES2020
// any ES2021
// try 提案阶段
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
status = PENDING
value = null
reason = null
onFulfilledCallback = []
onRejectedCallback = []
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
while (this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(value)
}
}
}
reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
while (this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(reason)
}
}
}
then(onFulfilled, onRejected) {
const onFulfilled1 =
typeof onFulfilled === 'function' ? onFulfilled : (value) => value
const onRejected1 =
typeof onRejected === 'function'
? onRejected
: (reason) => {
throw reason
}
const promise2 = new MyPromise((resolve, reject) => {
const onResolve = () => {
queueMicrotask(() => {
try {
const x = onFulfilled1(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
})
}
const onReject = () => {
queueMicrotask(() => {
try {
const x = onRejected1(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
})
}
if (this.status === FULFILLED) {
onResolve()
} else if (this.status === REJECTED) {
onReject()
} else if (this.status === PENDING) {
this.onFulfilledCallback.push(onResolve)
this.onRejectedCallback.push(onReject)
}
})
return promise2
}
// 只对 reject 做处理
catch(onRejected) {
return this.then(undefined, onRejected)
}
// ES2018
finally(fn) {
return this.then(
(value) => {
return MyPromise.resolve(fn()).then(() => value)
},
(err) => {
return MyPromise.resolve(fn()).then(() => {
throw err
})
}
)
}
static resolve(parameter) {
if (parameter instanceof MyPromise) {
return parameter
}
return new MyPromise((resolve) => {
resolve(parameter)
})
}
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
// 1. 传入空的可迭代对象 返回已完成 resolved
// 2. 传入的参数不包含 promise,返回异步完成 resolved
// 3. 所有都都成功或任何一个失败,就变为 resolved 或 reject
static all(promiseList = []) {
return new MyPromise((resolve, reject) => {
const result = []
const length = promiseList.length
let count = 0
if (length === 0) {
return resolve(result)
}
promiseList.forEach((promise, index) => {
MyPromise.resolve(promise).then(
(value) => {
count++
result[index] = value
if (count === length) {
resolve[result]
}
},
(reason) => {
reject(reason)
}
)
})
})
}
// 1. 传入空的可迭代对象,则返回的 promise 将永远等待
// 2. 返回第一个结果值(无论 resolved 或 rejected)
static race(promiseList = []) {
return new MyPromise((resolve, reject) => {
const length = promiseList.length
if (length === 0) {
return // 永远 pending 中
// 思考 这种永远 pending 会有什么影响
// return resolve()
}
for (let i = 0; i < length; i++) {
MyPromise.resolve(promiseList[i]).then(
(value) => {
return resolve(value)
},
(reason) => {
return reject(reason)
}
)
}
})
}
// 所有都完成(无论是 resolved 或 rejected)才返回结果(包含每个 promise 的结果和状态)
static allSettled(promiseList) {
return new MyPromise((resolve, reject) => {
const result = []
const length = promiseList.length
let count = 0
if (length === 0) {
return resolve(result)
}
for (let i = 0; i < length; i++) {
const currentPromise = MyPromise.resolve(promiseList[i])
currentPromise.then(
(value) => {
count++
result[i] = {
status: FULFILLED,
value
}
if (count === length) return resolve(result)
},
(reason) => {
count++
result[i] = {
status: REJECTED,
value
}
if (count === length) return resolve(result)
}
)
}
})
}
// 这个方法和 Promise.all() 是相反的。
// 1. 传入空的可迭代对象 返回已失败 rejected
// 2. 传入的参数不包含 promise,返回异步完成 resolved
// 3. 任何一个成功或所有都失败,返回 resolved 或 reject
//
// https://github.com/es-shims/Promise.any/blob/main/implementation.js
static any(values) {
return new MyPromise((resolve, reject) => {
let hasResolved = false
let promiseLikes = []
let iterableCount = 0
let rejectionReasons = []
function resolveOnce(value) {
if (!hasResolved) {
hasResolved = true
resolve(value)
}
}
function rejectionCheck(reason) {
// TODO: 这样不能保证结果的顺序
rejectionReasons.push(reason)
if (rejectionReasons.length >= iterableCount) reject(rejectionReasons)
}
for (let value of values) {
iterableCount++
promiseLikes.push(value)
}
for (let promiseLike of promiseLikes) {
if (promiseLike.then !== undefined || promiseLike.catch !== undefined) {
promiseLike.then((result) => resolveOnce(result)).catch((err) => {})
promiseLike.catch((reason) => rejectionCheck(reason))
} else {
resolveOnce(promiseLike)
}
}
})
}
// Promise.try 让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API
// https://es6.ruanyifeng.com/#docs/promise#Promise-try
// https://github.com/tc39/proposal-promise-try/blob/main/polyfill.js
static try(func) {
return new MyPromise((resolve, reject) => {
resolve(func())
})
}
// 让同步函数同步执行,异步函数异步执行 是一种场景
// 还有一种场景,比如请求了接口,但数据做了缓存,两次返回,一次是异步,一次是同步
// 参见 https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_DOM_API/Microtask_guide#%E4%BD%95%E6%97%B6%E4%BD%BF%E7%94%A8%E5%BE%AE%E4%BB%BB%E5%8A%A1
}
function resolvePromise(p, x, resolve, reject) {
if (p === x) {
return reject(
new TypeError('Chaining cycle detected for promise #<Promise>')
)
}
// 按规范 2.3.3 ~ 2.3.4 对以下逻辑进行处理
if (typeof x === 'object' || typeof x === 'function') {
if (x === null) return resolve(x)
let then
try {
then = x.then
} catch (err) {
return reject(err)
}
if (typeof then === 'function') {
let called = false
try {
then.call(
x,
(y) => {
if (called) return
called = true
resolvePromise(p, y, resolve, reject)
},
(r) => {
if (called) return
called = true
reject(r)
}
)
} catch (err) {
if (called) return
reject(err)
}
} else {
resolve(x)
}
} else {
resolve(x)
}
}
// 单测需要附加以下代码
MyPromise.deferred = function () {
const result = {}
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve
result.reject = reject
})
return result
}
module.exports = MyPromise
注意
ES6 Promise 是基于 EcmaScript 规范 而不是 Promise A+ 规范 来实现的
手写了 Promise 已经包含了所有功能,再熟悉下,仅实现以下功能呢
- 手写 Promise.all
- 手写 Promise.race
- 手写 Promise.allSettled
- 手写 Promise.any
- 手写 Promise.try
- 手写 Promise.deferred
- 手写 Promise.map
- p-limit
- p-queue
- p-throttle
js
MyPromise.prototype.finally = function (cb) {
return this.then(
(value) => {
return MyPromise.resolve(fn()).then(() => value)
},
(err) => {
return MyPromise.resolve(fn()).then(() => {
throw err
})
}
)
}
js
// 1. 传入空的可迭代对象 返回已完成 resolved
// 2. 传入的参数不包含 promise,返回异步完成 resolved
// 3. 所有都都成功或任何一个失败,就变为 resolved 或 reject
export function pAll(promiseList = []) {
return new Promise((resolve, reject) => {
const result = []
const length = promiseList.length
let count = 0
if (length === 0) {
return resolve(result)
}
promiseList.forEach((promise, index) => {
Promise.resolve(promise).then(
(value) => {
count++
result[index] = value
if (count === length) {
resolve[result]
}
},
(reason) => {
reject(reason)
}
)
})
})
}
js
// 1. 传入空的可迭代对象,则返回的 promise 将永远等待
// 2. 返回第一个结果值(无论 resolved 或 rejected)
export function pRace(promiseList = []) {
return new Promise((resolve, reject) => {
const length = promiseList.length
if (length === 0) {
return // 永远 pending 中
// 思考 这种永远 pending 会有什么影响
// return resolve()
}
for (let i = 0; i < length; i++) {
Promise.resolve(promiseList[i]).then(
(value) => {
return resolve(value)
},
(reason) => {
return reject(reason)
}
)
}
})
}
js
// 所有都完成(无论是 resolved 或 rejected)
// 才返回结果(包含每个 promise 的结果和状态)
export function pAllSettled(promiseList = []) {
return new Promise((resolve, reject) => {
const result = []
const length = promiseList.length
let count = 0
if (length === 0) {
return resolve(result)
}
for (let i = 0; i < length; i++) {
const currentPromise = Promise.resolve(promiseList[i])
currentPromise.then(
(value) => {
count++
result[i] = {
status: FULFILLED,
value
}
if (count === length) return resolve(result)
},
(reason) => {
count++
result[i] = {
status: REJECTED,
value
}
if (count === length) return resolve(result)
}
)
}
})
}
js
// 这个方法和 Promise.all() 是相反的。
// 1. 传入空的可迭代对象 返回已失败 rejected
// 2. 传入的参数不包含 promise,返回异步完成 resolved
// 3. 任何一个成功或所有都失败,返回 resolved 或 reject
//
// https://github.com/es-shims/Promise.any/blob/main/implementation.js
export function pAny(values) {
return new Promise((resolve, reject) => {
let hasResolved = false
let promiseLikes = []
let iterableCount = 0
let rejectionReasons = []
function resolveOnce(value) {
if (!hasResolved) {
hasResolved = true
resolve(value)
}
}
function rejectionCheck(reason) {
// TODO: 这样不能保证结果的顺序
rejectionReasons.push(reason)
if (rejectionReasons.length >= iterableCount) reject(rejectionReasons)
}
for (let value of values) {
iterableCount++
promiseLikes.push(value)
}
for (let promiseLike of promiseLikes) {
if (promiseLike.then !== undefined || promiseLike.catch !== undefined) {
promiseLike.then((result) => resolveOnce(result)).catch((err) => {})
promiseLike.catch((reason) => rejectionCheck(reason))
} else {
resolveOnce(promiseLike)
}
}
})
}
js
// Promise.try 让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API
// https://es6.ruanyifeng.com/#docs/promise#Promise-try
// https://github.com/tc39/proposal-promise-try/blob/main/polyfill.js
export function pTry(func) {
return new Promise((resolve, reject) => {
resolve(func())
})
}
// 让同步函数同步执行,异步函数异步执行 是一种场景
// 还有一种场景,比如请求了接口,但数据做了缓存,两次返回,一次是异步,一次是同步
// 参见 https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_DOM_API/Microtask_guide#%E4%BD%95%E6%97%B6%E4%BD%BF%E7%94%A8%E5%BE%AE%E4%BB%BB%E5%8A%A1
js
// 返回 Deferred(延迟) 的 Promise(承诺)对象
// Deferred 主要是用于内部,来维护异步模型的状态
export function pDeferred() {
const result = {}
result.promise = new Promise(function (resolve, reject) {
result.resolve = resolve
result.reject = reject
})
return result
}
// usage:
function asyncDoSomeing(flag, message) {
var deferred = new pDeferred()
setTimeout(function () {
if (flag) {
deferred.resolve(message)
} else {
deferred.reject({ code: 400, message: '拒绝' })
}
}, 3000)
return deferred.promise
}
asyncDoSomeing(true, '测试执行成功').then(
function (message) {
console.log(message)
},
function (err) {
console.log(err)
}
)
js
// 实现限制 promise 并发数
export function pMap(list, mapper, concurrency = Infinity) {
// list 为 Iterator,先转化为 Array
list = Array.from(list)
return new Promise((resolve, reject) => {
let currentIndex = 0
let result = []
let resolveCount = 0
let len = list.length
function next() {
const index = currentIndex
currentIndex++
Promise.resolve(list[index])
.then((o) => mapper(o, index))
.then((o) => {
result[index] = o
resolveCount++
})
.finally(() => {
if (resolveCount === len) {
resolve(result)
}
if (currentIndex < len) {
next()
}
})
// catch 时没处理
}
for (let i = 0; i < concurrency && i < len; i++) {
next()
}
})
}
// usage:
const sleep = (...rest) =>
new Promise((resolve) => setTimeout(resolve, ...rest))
const tasks = [1, 2, 3, 4, 5, 6]
console.time('pMap')
pMap(tasks, (task) => sleep(1000, task), 3).then((res) => {
console.log(res)
console.timeEnd('pMap')
})
js
// p-limit 是一个限制并发的库,传入任务,返回一个函数用于接收每一个异步任务,
// 保证无论传入多少个异步任务,都始终根据传入的并发数量来进行执行。
// 这里直接拿来参考学习,源码如下
// https://github.com/sindresorhus/p-limit
import Queue from 'yocto-queue'
export function pLimit(concurrency) {
// 验证并发数的合法性
if (
!(
(Number.isInteger(concurrency) ||
concurrency === Number.POSITIVE_INFINITY) &&
concurrency > 0
)
) {
throw new TypeError('Expected `concurrency` to be a number from 1 and up')
}
// 这个是链表实现的队列,enqueue 入队,dequeue 出队
const queue = new Queue()
let activeCount = 0
const next = () => {
activeCount--
if (queue.size > 0) {
queue.dequeue()()
}
}
const run = async (fn, resolve, args) => {
activeCount++
const result = (async () => fn(...args))()
resolve(result)
try {
await result
} catch {}
next()
}
const enqueue = (fn, resolve, args) => {
queue.enqueue(run.bind(undefined, fn, resolve, args))
;(async () => {
// This function needs to wait until the next microtask before comparing
// `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
// when the run function is dequeued and called. The comparison in the if-statement
// needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
await Promise.resolve()
if (activeCount < concurrency && queue.size > 0) {
queue.dequeue()()
}
})()
}
const generator = (fn, ...args) =>
new Promise((resolve) => {
enqueue(fn, resolve, args)
})
Object.defineProperties(generator, {
activeCount: {
get: () => activeCount
},
pendingCount: {
get: () => queue.size
},
clearQueue: {
value: () => {
queue.clear()
}
}
})
return generator
}
// usage:
const limit = pLimit(2)
const tasks = [1, 2, 3].map((i) => () => {
sleep(i)
})
Promise.all(tasks.map((task) => limit(task))).then((res) => {
console.log(res)
})
// 我们自己也实现个,参考 pMap(这里用数组缓存任务)
function pLimit(promiseTasks = [], concurrency) {
const tasks = Array.from(promiseTasks)
return new Promise((resolve, reject) => {
let result = []
let currentIndex = 0
let resolveCount = 0
let len = tasks.length
function next() {
const index = currentIndex
currentIndex++
Promise.resolve(tasks[index]())
.then((res) => {
result[index] = res
resolveCount++
})
.catch((err) => {
result[index] = err
resolveCount++
})
.finally(() => {
// 此处 resolveCount === currentIndex
if (resolveCount === len) resolve(result)
if (currentIndex < len) next()
})
}
for (let i = 0; i < concurrency && i < len; i++) {
next()
}
})
}
// usage:
const sleep = (...rest) =>
new Promise((resolve) => setTimeout(resolve, ...rest))
const tasks = [1, 2, 3, 4, 5, 6].map((task) => () => sleep(1000, task))
console.time('pLimit')
pLimit(tasks, 2).then((res) => {
console.log(res)
console.timeEnd('pLimit')
})
// 这实现与 pMap 有点一样啊?思考下差异
// 这里 PLimit 仅有 limit 功能,没有 map 功能,
// 而 pLimit 同时有 map 和 limit 功能
关于 Promise.try()
让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API
js
// 方案一
const f = () => console.log('now')
;(async () => f())()
console.log('next')
// now
// next
// 方案二
const f = () => console.log('now')
;(() => new Promise((resolve) => resolve(f())))()
console.log('next')
// now
// next
// Promise.try 提案
const f = () => console.log('now')
Promise.try(f)
console.log('next')
// now
// next
小练习
js
// 自己造的一个题(2022-07-26 by cloudyan)
Promise.resolve(Promise.reject(1))
.catch((err) => {
console.log('catch1:', err)
return err
})
.then((res) => {
console.log('then1:', res)
return res
})
.finally((res) => {
console.log('finally:', res)
return res
})
.then((res) => {
console.log('then2:', res)
return res
})
.catch((err) => {
console.log('catch2:', err)
return err
})
加强练习
其他