一、前言
Promise 作为前端开发必不可少的工具,频频出现在面试题中,本文将会带领读者实现一个Promise对象,帮助读者从底层理解其构成。学后记得结合一些经典的面试题来巩固一下奥👊~
二、源码手撕
2.1 基本定义
需求:
1.定义 Promise 对象,在构造函数中定义 resolve 和 reject 方法
2.在构造函数中定义 state 参数,初始值为 ‘pending’,同时设有 ‘fulfiled’,‘rejected’ 共三种状态
3.在构造函数中定义 success,fail 参数,分别对应传入 resolve 和 reject 的值
注意:
1.Promise 的 state 只能改变一次
2.应该使用 try catch 语法完善 executor(resolve,reject) 的执行
综上,写出的代码如下:
class MyPromise {
constructor(executor) {
this.state = 'pending' // 记录Promise对象的状态
this.success = null // 记录成功的值 用then调用
this.fail = null // 记录失败的值 用catch调用
const reslove = (success) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.success = success
}
}
const reject = (fail) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.fail = fail
}
}
// 如果执行器中出现错误 直接将promise改为失败状态
try {
executor(reslove, reject)
} catch (error) {
reject(error)
}
}
}
const p = new MyPromise((reslove, reject) => {
reslove(6)
})
p.then((res) => {
console.log(res)
})
当然目前的代码还无法运行,接下来让我们完善 then()的调用
2.2 then()、异步操作
首先解决 then() 的问题
需求:
1.传入 onFulfiled,onRejected 方法
2.用 onFulfiled,onRejected 接收 p.then() 传入的 success 和 fail
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') onFulfilled(this.success)
if (this.state === 'rejected') onRejected(this.fail)
}
与此同时,还需要加入异步操作,才能保证 then 执行时,state 不为‘pending’状态(reslove可能被异步执行)
需求:
1.then() 的执行不直接输出结果,而是将得到的数据先全部存放在两个数组中(成功和失败),在 resolve 或 reject 执行后再将数组中的函数输出
2.需要增加一个 if 判断 state 如果是 pending,将数据存储在数组中
3.在 resolve 和 reject 方法中遍历两数组得到数据
注意: (这个真的很重要!!!🙉🙉🙉)
①函数在数组中的存储需要用到函数的闭包(在函数的内部应用到外部的参数)——利用闭包将 success 或 fail 储存在函数中,并整合至数组
综上,代码如下:
class MyPromise {
constructor(executor) {
this.state = 'pending' // 记录Promise对象的状态
this.success = null // 记录成功的值 用then调用
this.fail = null // 记录失败的值 用catch调用
// 记录成功、失败的回调函数数组(通过闭包存储了success、fail值)
this.resloveCallbacks = []
this.rejectCallbacks = []
const resolve = (success) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.success = success
// 调用记录的回调函数
while (this.resolveCallbacks.length) {
this.resolveCallbacks.shift()(value)
}
}
}
const reject = (fail) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.fail = fail
// 调用记录的回调函数
while (this.rejectCallbacks.length) {
this.rejectCallbacks.shift()(value)
}
}
}
// 如果执行器中出现错误 直接将promise改为失败状态
try {
executor(reslove, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') onFulfilled(this.success)
if (this.state === 'rejected') onRejected(this.fail)
if (this.state === 'pending') {
this.resloveCallbacks.push(() => {
onFulfilled(this.success)
})
this.rejectCallbacks.push(() => {
onRejected(this.fail)
})
}
}
}
const p = new MyPromise((reslove, reject) => {
setTimeout(() => {
reslove(6)
}, 500)
})
p.then((res) => {
console.log(res)
})
// 输出:0.5s后打印 6
2.2 链式调用
接下来就是最关键的链式调用部分
需求:
1.上一个 .then 要返回一个 Promise 对象
2.下一个 .then 的参数,要拿到上一个 .then 的回调返回值
3.设置参数 item 为 onFulfilled 成功的 .then 回调的返回值,并通过 resolve(item) 来进行链式操作
4.参照 fulfilled 的处理方式,改造 rejected 和 pending
5.then() 可以不传参数
注意:
1.代码运用了递归,上一级 then 返回 Promsie 对象来延续下一级的 then,因此需要在 then 函数中构造一个新的 Promise 对象并返回,在新的 Promise 对象中通过当前阶段的回调函数值决定下一步操作
2.item 可能是传入的普通值,也可能是 Promise 对象,因此要判断其类型并进行相应的处理
3.为避免循环调用(情景如下代码),通过判断语句(item === promiseNext ?)来解决以上矛盾,然而判断逻辑写在 promiseNext 内部,此时 promiseNext 未完成初始化,因此可以借助宏任务或微任务,将数据传入一个函数中进行判断,在此我们使用微任务 queueMicrotask 进行处理(当然也可以使用setTimeout 0ms 处理)
const p2 = p.then((data)=>{
console.log(data);
return p2
})
综上,代码如下:
class MyPromise {
constructor(executor) {
this.state = 'pending' // 记录Promise对象的状态
this.success = null // 记录成功的值 用then调用
this.fail = null // 记录失败的值 用catch调用
// 记录成功、失败的回调函数数组(通过闭包存储了success、fail值)
this.resolveCallbacks = []
this.rejectCallbacks = []
const resolve = (success) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.success = success
// 调用记录的回调函数
while (this.resolveCallbacks.length) {
this.resolveCallbacks.shift()(value)
}
}
}
const reject = (fail) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.fail = fail
// 调用记录的回调函数
while (this.rejectCallbacks.length) {
this.rejectCallbacks.shift()(value)
}
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
// 处理不传参数的情况 不传则使用默认值
onFulfilled = (typeof onFulfilled === 'function') ? onFulfilled : success => success;
onRejected = (typeof onRejected === 'function') ? onRejected : fail => { throw fail };
let promiseNext = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
// 通过微任务确保 promiseNext 初始化完成
// 使用try catch 返回error时可以捕获抛出
queueMicrotask(() => {
try {
let item = onFulfilled(this.success)
resolvePromise(item, resolve, reject, promiseNext)
} catch (error) {
reject(error)
}
})
}
if (this.state === 'rejected') {
queueMicrotask(() => {
try {
const item = onRejected(this.reason);
// 传入 resolvePromise 集中处理
resolvePromise(item, resolve, reject, promiseNext)
} catch (error) {
reject(error)
}
})
}
if (this.state === 'pending') {
this.resolveCallbacks.push(() => {
queueMicrotask(() => {
try {
let item = onFulfilled(this.success)
resolvePromise(item, resolve, reject, promiseNext)
} catch (error) {
reject(error)
}
})
})
this.rejectCallbacks.push(() => {
try {
const item = onRejected(this.reason);
// 传入 resolvePromise 集中处理
resolvePromise(item, resolve, reject, promiseNext)
} catch (error) {
reject(error)
}
})
}
})
return promiseNext
}
}
const resolvePromise = (x, resolve, reject, promiseNext) => {
//处理循环调用
if (x === promiseNext) {
const err = new TypeError('Uncaught (in promsie) TypeError:detected for promise #<Promsie>')
console.error(err)
return reject(err)
}
//判断 x 是否是 MyPromise 对象
if (x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
const p = new MyPromise((reslove, reject) => {
reslove('p')
})
const q = new MyPromise((reslove, reject) => {
reslove('q')
})
p.then((res) => {
console.log(res)
return q
}).then((res) => {
console.log(res)
})
// 输出:
// p
// q
const p2 = p.then((data) => {
console.log(data);
return p2
})
// 输出:
// p
// TypeError: Uncaught (in promsie) TypeError:detected for promise #<Promsie>
2.3 实现 resolve 和 reject
比较简单,直接上代码
// resolve 静态方法
static resolve(success) {
if (success instanceof MyPromise) {
return success;
}
return new MyPromise(resolve => {
resolve(success);
});
}
// reject 静态方法
static reject(fail) {
if (fail instanceof MyPromise) {
return fail;
}
return new MyPromise((resolve, reject) => {
reject(fail);
});
}
2.4 实现剩余方法
1.finally:无论传入的是 resolve 还是 reject 都会输出 (一般用于Promise出错后的善后措施,如某些计时器的关闭等)
static finally(fn) {
return this.then(
(success) => {
fn()
return success
},
(fail) => {
fn()
throw fail
});
};
2.catch: 抛出错误
static catch(onRejected) {
return this.then(null, onRejected)
}
3.all:接受一个 Promise 数组,当所有 Promise 状态 resolve 后,执行 resolve
static all(promises) {
return new MyPromise((resolve, reject) => {
if (promises.length === 0) {
resolve([])
} else {
let result = []
let index = 0;
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(data) => {
result[i] = data
if (++index === promises.length) {
resolve(result)
}
},
(err) => {
reject(err)
return
})
}
}
})
}
4.race:接受一个 promise 数组,当有一个 promise 状态 resolve 后,执行resolve(谁快谁先执行)
static race(promises) {
return new MyPromise((resolve, reject) => {
if (promises.length === 0) {
resolve()
} else {
let index = 0
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(data) => {
resolve(data)
},
(err) => {
reject(err)
return
})
}
}
})
}
至此,Promise已经大功告成,以下是完整的代码:
class MyPromise {
constructor(executor) {
this.state = 'pending' // 记录Promise对象的状态
this.success = null // 记录成功的值 用then调用
this.fail = null // 记录失败的值 用catch调用
// 记录成功、失败的回调函数数组(通过闭包存储了success、fail值)
this.resolveCallbacks = []
this.rejectCallbacks = []
const resolve = (success) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.success = success
// 调用记录的回调函数
while (this.resolveCallbacks.length) {
this.resolveCallbacks.shift()(success)
}
}
}
const reject = (fail) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.fail = fail
// 调用记录的回调函数
while (this.rejectCallbacks.length) {
this.rejectCallbacks.shift()(fail)
}
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
// 处理不传参数的情况 不传则使用默认值
onFulfilled = (typeof onFulfilled === 'function') ? onFulfilled : success => success;
onRejected = (typeof onRejected === 'function') ? onRejected : fail => { throw fail };
let promiseNext = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
// 通过微任务确保 promiseNext 初始化完成
// 使用try catch 返回error时可以捕获抛出
queueMicrotask(() => {
try {
let item = onFulfilled(this.success)
resolvePromise(item, resolve, reject, promiseNext)
} catch (error) {
reject(error)
}
})
}
if (this.state === 'rejected') {
queueMicrotask(() => {
try {
const item = onRejected(this.reason);
// 传入 resolvePromise 集中处理
resolvePromise(item, resolve, reject, promiseNext)
} catch (error) {
reject(error)
}
})
}
if (this.state === 'pending') {
this.resolveCallbacks.push(() => {
queueMicrotask(() => {
try {
let item = onFulfilled(this.success)
resolvePromise(item, resolve, reject, promiseNext)
} catch (error) {
reject(error)
}
})
})
this.rejectCallbacks.push(() => {
try {
const item = onRejected(this.reason);
// 传入 resolvePromise 集中处理
resolvePromise(item, resolve, reject, promiseNext)
} catch (error) {
reject(error)
}
})
}
})
return promiseNext
}
// resolve 静态方法
static resolve(success) {
if (success instanceof MyPromise) {
return success;
}
return new MyPromise(resolve => {
resolve(success);
});
}
// reject 静态方法
static reject(fail) {
if (fail instanceof MyPromise) {
return fail;
}
return new MyPromise((resolve, reject) => {
reject(fail);
});
}
static finally(fn) {
return this.then(
(success) => {
fn()
return success
},
(fail) => {
fn()
throw fail
});
}
static catch(onRejected) {
return this.then(null, onRejected)
}
static all(promises) {
return new MyPromise((resolve, reject) => {
if (promises.length === 0) {
resolve([])
} else {
let result = []
let index = 0;
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(data) => {
result[i] = data
if (++index === promises.length) {
resolve(result)
}
},
(err) => {
reject(err)
return
})
}
}
})
}
static race(promises) {
return new MyPromise((resolve, reject) => {
if (promises.length === 0) {
resolve()
} else {
let index = 0
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(data) => {
resolve(data)
},
(err) => {
reject(err)
return
})
}
}
})
}
}
const resolvePromise = (x, resolve, reject, promiseNext) => {
//处理循环调用
if (x === promiseNext) {
const err = new TypeError('Uncaught (in promsie) TypeError:detected for promise #<Promsie>')
console.error(err)
return reject(err)
}
//判断 x 是否是 MyPromise 对象
if (x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
const p = new MyPromise((reslove, reject) => {
reslove('p')
})
const q = new MyPromise((reslove, reject) => {
reslove('q')
})
p.then((res) => {
console.log(res)
return q
}).then((res) => {
console.log(res)
})
// 输出:
// p
// q
三、总结
读者们其实可以用一些 Promise 的验证工具来验证自己的 Promise 对象是否合规(如PromiseA+,详见百度~),相信在走完一遍流程之后,大家对 Promise 的认知会更进一步,本文中存在一些有误的信息也欢迎读者指出,本菜鸟会速速修正的!