当面试官说出这句话的时候,有些同学感觉可能天都塌了。其实手搓promise并不难,我们得关注promise身上的几个特点
- promise中的构造函数要传入一个回调函数,这个回调函数有两个参数resolve,reject分别代表成功和失败状态
- promise的
then()
方法,这个方法只有当前面的promise状态从pending变更为resolve才会依次执行,否则会一直等待。
接下来我们用js中的class来实现一下promise
1. promise
首先先创建一个我们的promise类,并且其中有一个构造函数,在这个构造函数中我们需要实现以下几个功能:
- 可以存放promise状态
- 存放成功的结果和失败的原因
- resolve和reject函数
- 如果是pending状态则要将后面的
.then()
全部储存起来直到状态变更为resolve或者reject后全部执行。
下面我们来实现一下promise的构造函数constructor
部分:
js
constructor(executor) {
this.status = 'pending' // 存放状态
this.value = null // 存放成功的结果
this.reason = null // 存放失败的原因
this.onFullfilledCallbacks = [] // 存放成功的回调
this.onRejectedCallbacks = [] // 存放失败的回调
const resolve = (value) => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
this.onFullfilledCallbacks.forEach(fn => fn(value)) // 执行成功回调
}
}
const reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
this.onRejectedCallbacks.forEach(fn => fn(reason)) // 执行失败回调
}
}
executor(resolve, reject) // 调用传入的箭头函数
}
2. then
在写 promise 的 then 方法的时候我们得注意一点 then 方法的返回值是一个新的 promise,状态为 pending, then的pending状态会根据上一个promise的状态修改而修改。除此之外我们还得注意以下几点
then 的原理:
- 接受两个参数,第一个是成功的回调,第二个是失败的回调
- 返回一个新的promise
- 当 then 执行到的时刻,then前面的promise状态已经变更为fulfilled或rejected,then中的回调函数由then自己执行
- 当 then 执行到的时刻,then前面的promise状态是pending,then中的回调函数会先被存放,等待promise状态变更为fulfilled或rejected,再由resolve或reject函数执行
首先我们需要把then挂在promise的原型上面,并且这个方法有两个参数,一个是成功的回调一个是失败的回调。注意,这时候我们在进行后面的逻辑时,需要先判断传入then中的参数是不是方法,如果不是某个方法的话它就在then中啥都不会干,下面给大家展示一下:
js
function A() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('a');
resolve('a')
}, 1000);
})
}
function B() {
console.log('b');
}
A().then(
[],
[]
)
// 输出:
// a
在了解完了这一点之后,我们先来完成then函数前面一部分:
js
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
let newPromise = new MyPromise((resolve, reject) => {
})
return newPromise
}
tips: new MyPromise()中一定要传入箭头函数,因为箭头函数中没有this,这样的话才能通过this获取到上一个promise的状态
resolve和reject状态
接下来我们来完成这个传入箭头函数中的逻辑,在这个函数中,我们需要判断上一个promise对象的状态从而执行对应的操作,如果状态是fulfilled
或者rejected
那么它执行的操作都是一样的,下面我们就来以状态为fulfilled为例子。
首先我们得注意一点,promise.then()
这个方法在js中是微任务属于异步代码,但是由于js中的微任务打造太难,所以我们先将其打造成宏任务代码,在这里我们使用setTimeout
来包裹里面的逻辑。除此之外,在then()
参数中的回调函数中我们还可以获取上一个promise通过resolve出来的结果,而这个结果我们存放在了value
当中,所以我们只需要将其传入这个回调函数中并执行这个结果即可。
在我们获取到了then()
中回调函数执行的结果之后,我们需要对其进行判断,如果返回出来的结果还是我们所打造的那个Promise,那么我们让其覆盖newPromise并且将这个结果传入构造函数中的resolve从而将其传递给下一个then(),接下来我们来看fulfilled和rejected的代码:
js
if (this.status === 'fulfilled') {
// 作为异步任务,微任务太难打造所以变成宏任务
setTimeout(() => {
try {
// 将前面resolve的值传入回调
const result = onFulfilled(this.value)
// 判断回调返回的结果是不是Promise
if (result instanceof MyPromise) {
newPromise = result
}
// 将结果传给下一个then()
resolve(result)
} catch (error) {
reject(error)
}
})
}
if (this.status === 'rejected') {
setTimeout(() => {
try {
const result = onRejected(this.reason) // 作为异步任务,微任务太难打造所以变成宏任务
if (result instanceof MyPromise) {
newPromise = result
}
resolve(result)
} catch (error) {
reject(error)
}
})
}
pending状态
在前文中我们说到,如果promise的状态为pending
,就要把后面then中的逻辑全部都放入对应的数组中,当状态发生变更时就清空对应状态的数组,并且其中的代码都是异步的,这时候我们同上需要用setTimeout
进行包裹,而这两个数组中的每个函数所执行的逻辑与上文中fulfilled和rejected状态时所执行的逻辑一样。下面我们来看状态为pending状态的时候该执行的逻辑:
js
if (this.status === 'pending') {
this.onFullfilledCallbacks.push((value) => {
setTimeout(() => {
try {
const result = onFulfilled(value)
if (result instanceof MyPromise) {
newPromise = result
}
resolve(result)
} catch (error) {
reject(error)
}
});
})
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
try {
const result = onRejected(value)
if (result instanceof MyPromise) {
newPromise = result
}
resolve(result)
} catch (error) {
reject(error)
}
});
})
}
完整代码
js
class MyPromise {
constructor(executor) {
this.status = 'pending' // 存放状态
this.value = null // 存放成功的结果
this.reason = null // 存放失败的原因
this.onFullfilledCallbacks = [] // 存放成功的回调
this.onRejectedCallbacks = [] // 存放失败的回调
const resolve = (value) => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
this.onFullfilledCallbacks.forEach(fn => fn(value)) // 执行回调
}
}
const reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
this.onRejectedCallbacks.forEach(fn => fn(reason)) // 执行回调
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
// 返回一个新的promise,这里一定要箭头函数,不然就是这个函数体的不是then的this了
let newPromise = new MyPromise((resolve, reject) => {
if (this.status === 'fulfilled') {
setTimeout(() => {
try {
const result = onFulfilled(this.value) // 作为异步任务,微任务太难打造所以变成宏任务
if (result instanceof MyPromise) {
newPromise = result
}
resolve(result)
} catch (error) {
reject(error)
}
})
}
if (this.status === 'rejected') {
setTimeout(() => {
try {
const result = onRejected(this.reason) // 作为异步任务,微任务太难打造所以变成宏任务
if (result instanceof MyPromise) {
newPromise = result
}
resolve(result)
} catch (error) {
reject(error)
}
})
}
if (this.status === 'pending') {
this.onFullfilledCallbacks.push((value) => {
setTimeout(() => {
try {
const result = onFulfilled(value)
if (result instanceof MyPromise) {
newPromise = result
}
resolve(result)
} catch (error) {
reject(error)
}
});
})
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
try {
const result = onRejected(value)
if (result instanceof MyPromise) {
newPromise = result
}
resolve(result)
} catch (error) {
reject(error)
}
});
})
}
})
return newPromise
}
}
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
console.log(22);
}, 1000);
resolve('success')
})
p.then(res => {
console.log(res);
})
总结
- promise中的构造函数要传入一个回调函数,这个回调函数有两个参数resolve,reject分别代表成功和失败状态
- promise的
then()
方法,这个方法只有当前面的promise状态从pending变更为resolve才会依次执行,否则会一直等待。 - then 方法的返回值是一个新的 promise,状态为 pending, then的pending状态会根据上一个promise的状态修改而修改
then 的原理:
- 接受两个参数,第一个是成功的回调,第二个是失败的回调
- 返回一个新的promise
- 当 then 执行到的时刻,then前面的promise状态已经变更为fulfilled或rejected,then中的回调函数由then自己执行
- 当 then 执行到的时刻,then前面的promise状态是pending,then中的回调函数会先被存放,等待promise状态变更为fulfilled或rejected,再由resolve或reject函数执行