Promise 的模拟实现
1. 为什么会有 Promise
当我们多次进行有依赖的网络请求或者文件请求时, 很可能会造成代码的层层嵌套, 导致回调地狱的出现:
$.ajax({
url: "xxx",
success: function(result) {
$.ajax({
url: result,
success: function(result) {
$.ajax({
url: result,
success: function(result) {
//...
}
})
}
})
}
})
n 重依赖的请求会导致出现 n 重嵌套, 为了解决回调地狱问题, JS 中有了 Promise
2. 如何实现 Promise
2.1 初步实现 Promise
-
为什么会有回调地狱的问题出现呢?
一个重要的原因就是请求所获得的结果只会出现在内层的函数中, 故而要使用该结果就必须得在请求中套用请求, 最终导致回调地狱的出现 -
故而, 解决回调地狱的第一步就是将请求所获取的结果从嵌套里提出来:
于是我们联想到 JQuery 的执行过程--将每次执行所获取的结果存储在 JQuery 实例对象的某一属性上. 这不仅将结果提取了出来, 还能够实现链式调用, 从而方便使用
class Promise {
constructor(executor) { // new Promise 时要传入一个有两个形参的函数
this.result = undefined // 设置实例对象的结果
const resolve = result => { // 当请求成功, 获得结果时, 调用 resolve, 设置结果到实例对象上
this.result = result
}
const reject = reason => { // 当请求失败时, 调用 reject, 将失败的原因记录到实例对象上
this.result = reason
}
executor(resolve, reject) // 执行传入的函数
}
then(onFulfilled, onRejected) { // 对于 then 方法的调用, 传入两个函数, 分别对成功和失败的两种情况进行处理
// ...
}
}
2.2 初步实现 then
我们容易发现, 在对 then 方法的编写中, 我们无从得知上次调用所获取的结果是成功的还是失败的, 为了解决这个问题, 想到两种方法:
- 额外设置一个属性
state
来表示成功或失败的状态 - 额外设置一个属性
reason
用来记录失败状态对应的结果(result
用来表示成功时所对应的结果), 当为一个属性赋值时, 就要清空另一个属性的值
我们暂且先使用第一种方法来完成对成功和失败状态的判断
class Promise {
constructor(executor) { // new Promise 时要传入一个有两个形参的函数
this.state = "pending" // 设置实例对象所对应的状态的
this.result = undefined // 设置实例对象的结果
const resolve = result => { // 改变状态和结果(当成功时调用)
this.state = "fulfilled"
this.result = result
}
const reject = reason => { // 改变状态和结果(当失败时调用)
this.state = "rejected"
this.result = reason
}
executor(resolve, reject) // 执行传入的函数
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => { // 保证链式调用
if (this.state === "pending") {
} else if (this.state === "fulfilled") {
onFulfilled(this.result, resolve, reject)
} else if (this.state === "rejected") {
onFulfilled(this.result, resolve, reject)
}
})
}
}
接着, 我们就发现我们面临着两个问题:
- 如果调用
then
时,Promise
实例对象的状态还没有确定怎么办?- 我们可以先将传入
then
中的方法存储到实例对象上, 等到实例对象的状态确定后, 再执行相应方法
- 我们可以先将传入
- 如果
onFulfilled
或onRejected
执行所获得的结果为Promise
实例对象怎么办?- 这乍一看好像并不是什么问题, 如果
p1.then
中的方法返回的是Promise
实例对象p2
的话, 就把p2
赋值给p1.result
不就行了吗 - 但这样是不行的. 假设在对实例对象
p1
进行处理时, 相应的函数返回了实例对象p2
, 那就会形成p1.result === p2
的情况, 如果要获取p2
的值, 则会出现嵌套的情况, 而嵌套过多的话, 最终就会形成回调地狱p1.then(fn1, fn2) // p1(p2) .then(p2 => { p2.then(p3 => { p3.then(p4 => { // ... }) }) })
- 这乍一看好像并不是什么问题, 如果
2.3 初步实现 resolvePromise
那么如何解决这个问题呢?
既然这个问题出现的原因就是 onFulfilled
和 onRejected
获取的结果是 Promise
实例对象导致的, 那我们是不是可以处理一下这个 Promise
实例对象, 直接获取其中的结果呢(若是 onFulfilled/onRejected
所获取的 result
无法返回, 那么直接返回 new Promise
并在其中进行 resolve/reject
处理即可)
function resolvePromise(x, resolve, reject) {
if (typeof x === "object" && x != null || typeof x === "function") { // x 为引用类型值
const then = x.then // 若 x 为 Promise 实例对象, 则其一定会有 then 方法
if (typeof then === "function") {
then.call(x, result => { // 若 x 的状态为成功, 则其对应的结果有可能会是 Promise 实例对象, 所以还需要继续递归调用 resolvePromise 方法
resolvePromise(result, resolve, reject)
}, reason => { // 若 x 的状态为失败, 则其对应的结果不会是 Promise 实例对象, 故而可以直接调用 reject(reason)
reject(reason)
})
} else { // x 没有 then 方法, 不会是 Promise 实例对象
resolve(x)
}
} else { // x 为基础类型值, 不会是 Promise 实例对象
resolve(x)
}
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => { // 保证链式调用
if (this.state === "pending") {
this.onFulfilledList.push(() => {
const x = onFulfilled(this.result)
resolvePromise(x, resolve, reject)
})
this.onRejectedList.push(() => {
const x = onRejected(this.result)
resolvePromise(x, resolve, reject)
})
} else if (this.state === "fulfilled") { // 当调用对象的状态为fulfilled时
const x = onFulfilled(this.result)
resolvePromise(x, resolve, reject)
} else if (this.state === "rejected") { // 当调用对象的状态为rejected时
const x = onFulfilled(this.result)
resolvePromise(x, resolve, reject)
}
})
}
constructor(executor) { // new Promise 时要传入一个有两个形参的函数
this.state = "pending" // 设置实例对象所对应的状态的
this.result = undefined // 设置实例对象的结果
this.onFulfilledList = []
this.onRejectedList = []
/* 由于可能会出现一个 Promise 实例对象对应多个 then 方法的情况, 所以要将存储方法的属性设置为数组
* let p1 = new Promise((resolve, reject) => {setTimeout(resolve(1))})
* p1.then(onFulfilled1)
* p1.then(onFulfilled2)
* */
const resolve = result => { // 改变状态和结果(当成功时调用)
this.state = "fulfilled"
this.result = result
this.onFulfilledList.forEach(fn => fn())
}
const reject = reason => { // 改变状态和结果(当失败时调用)
this.state = "rejected"
this.result = reason
this.onRejectedList.forEach(fn => fn())
}
executor(resolve, reject) // 执行传入的函数
}
再对这些方法进行一些加工处理, 就可以得到初版本的 Promise 实现了
2.4 Promise 初版本
class Promise {
constructor(executor) { // new Promise 时要传入一个有两个形参的函数
this.state = "pending" // 设置实例对象所对应的状态的
this.result = undefined // 设置实例对象的结果
this.onFulfilledList = []
this.onRejectedList = []
/* 由于可能会出现一个 Promise 实例对象对应多个 then 方法的情况, 所以要将存储方法的属性设置为数组
* let p1 = new Promise((resolve, reject) => {setTimeout(resolve(1))})
* p1.then(onFulfilled1)
* p1.then(onFulfilled2)
* */
const resolve = result => { // 改变状态和结果(当成功时调用)
if (this.state !== "pending") return
// 防止多次调用 resolve/reject 从而导致 onFulfilledList/onRejectedList 多次执行
this.state = "fulfilled"
this.result = result
this.onFulfilledList.forEach(fn => fn()) // 当执行 resolve 时, 会将一系列 then 中的 onFulfilled 方法执行
}
const reject = reason => { // 改变状态和结果(当失败时调用)
if (this.state !== "pending") return
// 防止多次调用 resolve/reject 从而导致 onFulfilledList/onRejectedList 多次执行
this.state = "rejected"
this.result = reason
this.onRejectedList.forEach(fn => fn()) // 当执行 reject 时, 会将一系列 then 中的 onRejected 方法执行
}
try { // 当 executor 执行出错时, 获取其出错的原因
executor(resolve, reject) // 执行传入的函数
} catch(err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
// 如果 onFulfilled 或 onRejected 并没有传函数进来, 则启用默认函数
if (typeof onFulfilled !== "function") onFulfilled = result => result
if (typeof onRejected !== "function") onRejected = reason => {throw reason}
return new Promise((resolve, reject) => { // 保证链式调用
if (this.state === "pending") {
this.onFulfilledList.push(() => {
try {
const x = onFulfilled(this.result)
resolvePromise(x, resolve, reject)
} catch(err) { // 获取出错原因
reject(err)
}
})
this.onRejectedList.push(() => {
try {
const x = onRejected(this.result)
resolvePromise(x, resolve, reject)
} catch(err) {
reject(err)
}
})
} else if (this.state === "fulfilled") { // 当调用对象的状态为fulfilled时
try {
const x = onFulfilled(this.result)
resolvePromise(x, resolve, reject)
} catch(err) { // 获取出错原因
reject(err)
}
} else if (this.state === "rejected") { // 当调用对象的状态为rejected时
try {
const x = onRejected(this.result)
resolvePromise(x, resolve, reject)
} catch(err) {
reject(err)
}
}
})
}
}
function resolvePromise(x, resolve, reject) {
if (typeof x === "object" && x != null || typeof x === "function") { // x 为引用类型值
const then = x.then // 若 x 为 Promise 实例对象, 则其一定会有 then 方法
if (typeof then === "function") {
try {
then.call(x, result => { // 若 x 的状态为成功, 则其对应的结果有可能会是 Promise 实例对象, 所以还需要继续递归调用 resolvePromise 方法
resolvePromise(result, resolve, reject)
}, reason => { // 若 x 的状态为失败, 则其对应的结果不会是 Promise 实例对象, 故而可以直接调用 reject(reason)
reject(reason)
})
} catch(err) {
reject(err)
}
} else { // x 没有 then 方法, 不会是 Promise 实例对象
resolve(x)
}
} else { // x 为基础类型值, 不会是 Promise 实例对象
resolve(x)
}
}
这个版本的 Promise
已经能够解决绝大多数的回调地狱的问题
new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1000)
resolve(200)
}, 1000)
}).then(result => {
console.log(233)
}).then(result => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(3000)
resolve(result << 1)
}, 3000)
})
}).then(result => console.log(result))
//=> 1000 233 3000 0
2.5 Promise/A+ 的 Promise 实现
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const resolvePromise = (promise2, x, resolve, reject) => {
// 防止出现 let p = promiseObj.then(result => p, reason => p) 的情况
if (promise2 === x) reject(new TypeError('cycling reference'))
// 用于区分 onFulfilled/onRejected 返回的 x 是否是引用类型,
if (typeof x === 'object' && x != null || typeof x === 'function') {
// 防止
let called = false
try {
const then = x.then
// 若 then 为函数, 则认为 x 为 Promise 的实例对象, 通过 then 来改变 promise2 的 result 和 state
if (typeof then === 'function') {
then.call(x, result => {
if (called) return
called = true
resolvePromise(promise2, result, resolve, reject)
}, reason => {
if (called) return
called = true
reject(reason)
})
} else { // 若 then 不是函数, 则认为 x 并不是 Promise 实例对象, 将 x 作为 promise2 的 result 来保存
resolve(x)
}
} catch(e) {
if (called) return
reject(e)
}
} else { // 若 x 不是引用类型, 则其必不是 Promise 实例对象, 则直接调用 resolve 方法, 令 promise2 的 result 为 x
resolve(x)
}
}
class Promise {
constructor(executor) {
this.state = PENDING
this.result = undefined
this.onFulfilledList = []
this.onRejectedList = []
const resolve = result => {
if (this.state !== PENDING) return
this.state = FULFILLED
this.result = result
this.onFulfilledList.forEach(fn => fn())
}
const reject = reason => {
if (this.state !== PENDING) return
this.state = REJECTED
this.result = reason
this.onRejectedList.forEach(fn => fn())
}
try {
executor(resolve, reject)
} catch(e) {
reject(e)
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') onFulfilled = result => result
if (typeof onRejected !== 'function') onRejected = reason => { throw reason }
const promise2 = new Promise((resolve, reject) => {
if (this.state === PENDING) {
this.onFulfilledList.push(() => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.result)
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
})
this.onRejectedList.push(() => {
queueMicrotask(() => {
try {
const x = onRejected(this.result)
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
})
} else if (this.state === FULFILLED) {
queueMicrotask(() => {
try {
const x = onFulfilled(this.result)
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
} else if (this.state === REJECTED) {
queueMicrotask(() => {
try {
const x = onRejected(this.result)
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
}
})
return promise2
}
}
2.5.1 promises-aplus-tests 测试
Promise.deferred = function() {
const result = {}
result.promise = new Promise(function(resolve, reject){
result.resolve = resolve
result.reject = reject
})
return result
}
module.exports = Promise
3. Promise 的其他方法
3.1 实例方法
3.1.1 Promise.prototype.catch
catch(onRejected) {
return this.then(_, onRejected)
}
3.1.2 Promise.prototype.finally
finally(fn) {
return this.then(fn, fn)
}
3.2 静态方法
3.2.1 Promise.resolve
static resolve(value) {
// 如果传入 Promise.resolve 的 value 为多层 Promise 嵌套, 则将嵌套拆除
if (value instanceof Promise) return value.then()
return new Promise((resolve, reject) => {
resolve(value)
})
}
3.2.2 Promise.reject
static reject(reason) {
// 如果传入 Promise.reject 的 reason 为多层 Promise 嵌套, 则将嵌套拆除
if (reason instanceof Promise) return reason.then()
return new Promise((resolve, reject) => {
reject(reason)
})
}
3.2.3 Promise.all
传入的是一个 promise 的 iterable 类型, 这里默认是数组类型
当所有 promsie 的状态变为 fulfilled 时, Promise.all 的状态也变为 fulfilled, 成员值为 promise 结果的一个数组
static all(promises) {
return new Promise((resolve, reject) => {
let ans = [], count = 0, len = promises.length
// 将每个成功的 promsie 的结果记录下来
const success = (result, idx) => {
ans[idx] = result
if (++ count === len) resolve(ans)
}
promises.forEach((p, idx) => { // 当 promise 实例成功时,调用 success 将其结果记录下来
p.then(result => { success(result, idx) }, reject)
})
})
}
3.2.4 Promise.allSettled
接收一个 promise 的 iterable 类型的迭代对象, 这里默认为数组
无论 iterable 中的 promise 的结果为 fulfilled 还是 rejected, 该方法都会将其记录下来, 当所有 promise 的状态确定后, 返回一个数组, 其成员为记录着相应 promise 结果和状态的对象
static allSettled(promises) {
return new Promise((resolve, reject) => {
let ans = [], count = 0, len = promises.length
// 根据不同的 promise 状态,生成不同的对象到数组中。当对象的数量足够时,确定 Promise.allSettled 返回的 promise 实例对象的状态
const finish = (result, state, idx) => {
ans[idx] = state === FULFILLED ?
{ status: state, value: result } :
{ status: state, reason: result }
if (++ count === len) resolve(ans)
}
promises.forEach((p, idx) => {
p.then(result => {
finish(result, FULFILLED, idx)
}, reason => {
finish(reason, REJECTED, idx)
})
})
})
}
3.2.5 Promise.race
返回的是第一个确定状态的 promise 实例对象的 result 和 state
static race(promises) {
return new Promsie((resolve, reject) => {
promises.forEach(p => {
p.then(resolve, reject)
})
})
}
4. Promise 及其各方法
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const resolvePromise = (promise2, x, resolve, reject) => {
// 防止出现 let p = promiseObj.then(result => p, reason => p) 的情况
if (promise2 === x) reject(new TypeError('cycling reference'))
// 用于区分 onFulfilled/onRejected 返回的 x 是否是引用类型,
if (typeof x === 'object' && x != null || typeof x === 'function') {
// 防止
let called = false
try {
const then = x.then
// 若 then 为函数, 则认为 x 为 Promise 的实例对象, 通过 then 来改变 promise2 的 result 和 state
if (typeof then === 'function') {
then.call(x, result => {
if (called) return
called = true
resolvePromise(promise2, result, resolve, reject)
}, reason => {
if (called) return
called = true
reject(reason)
})
} else { // 若 then 不是函数, 则认为 x 并不是 Promise 实例对象, 将 x 作为 promise2 的 result 来保存
resolve(x)
}
} catch(e) {
if (called) return
reject(e)
}
} else { // 若 x 不是引用类型, 则其必不是 Promise 实例对象, 则直接调用 resolve 方法, 令 promise2 的 result 为 x
resolve(x)
}
}
class Promise {
constructor(executor) {
this.state = PENDING
this.result = undefined
this.onFulfilledList = []
this.onRejectedList = []
const resolve = result => {
if (this.state !== PENDING) return
this.state = FULFILLED
this.result = result
this.onFulfilledList.forEach(fn => fn())
}
const reject = reason => {
if (this.state !== PENDING) return
this.state = REJECTED
this.result = reason
this.onRejectedList.forEach(fn => fn())
}
try {
executor(resolve, reject)
} catch(e) {
reject(e)
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') onFulfilled = result => result
if (typeof onRejected !== 'function') onRejected = reason => { throw reason }
const promise2 = new Promise((resolve, reject) => {
if (this.state === PENDING) {
this.onFulfilledList.push(() => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.result)
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
})
this.onRejectedList.push(() => {
queueMicrotask(() => {
try {
const x = onRejected(this.result)
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
})
} else if (this.state === FULFILLED) {
queueMicrotask(() => {
try {
const x = onFulfilled(this.result)
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
} else if (this.state === REJECTED) {
queueMicrotask(() => {
try {
const x = onRejected(this.result)
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
}
})
return promise2
}
catch(onRejected) {
return this.then(_, onRejected)
}
finally(fn) {
return this.then(fn, fn)
}
static resolve(result) {
if (result instanceof Promise) return result.then()
return new Promise((resolve, reject) => {
resolve(result)
})
}
static reject(reason) {
if (reason instanceof Promise) return reason.then()
return new Promise((resolve, reject) => {
reject(reason)
})
}
static all(promises) {
return new Promise((resolve, reject) => {
let ans = [], count = 0, len = promises.length
const success = (result, idx) => {
ans[idx] = result
if (++ count === len) resolve(ans)
}
promises.forEach((p, idx) => {
p.then(result => { success(result, idx) }, reject)
})
})
}
static allSettled(promises) {
return new Promise((resolve, reject) => {
let ans = [], count = 0, len = promises.length
const finish = (result, state, idx) => {
ans[idx] = state === FULFILLED ?
{ status: state, value: result } :
{ status: state, reason: result }
if (++ count === len) resolve(ans)
}
promises.forEach((p, idx) => {
p.then(result => {
finish(result, FULFILLED, idx)
}, reason => {
finish(reason, REJECTED, idx)
})
})
})
}
static race(promises) {
return new Promise((resolve, reject) => {
promises.forEach(p => p.then(resolve, reject))
})
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义