前言
手写 Promise 听说面试常考, 反正我现在没有遇到过,为了以防万一,先准备准备.
Promise 核心逻辑实现
我们先简单使用一下 promise ,假设你们都已经了解 promise 的 api 如何使用
let promise = new Promise((resolve, reject) => {
resolve('成功')
// reject('失败')
})
promise.then(value => {
console.log(value) // 成功
}, reason => {
console.log(reason)
})
初始化 MyPromise
Promise 是一个类,通过new 关键字实例化Promise,并传入了一个执行器函数,该函数接受两个参数,两个参数也分别都是函数,当其中一个执行之后状态就不会再改变. 这里引出 Promise有三种状态, pending:初始化状态, fullfilled;成功状态, rejected: 失败状态.状态只能从pending -> fullfilled 或者 pending -> rejected,一旦变为 fullfilled或者 rejected就不会再改变了.那么什么时候改变状态呢?接下来我们来实现一下源码
class MyPromise {
constructor(executor) {
// new Mypromise会立即执行 executor
// executor 中有两个方法分别是 resolve, reject
executor(this.resolve, this.reject)
}
resolve = () => {
}
reject = () => {
}
}
为 Mypromise 添加状态
先从简单开始, 我们要实现一个自己的 MyPromise, new 的时候传递了一个函数,并且是立即执行的,所以我们在 constructor传入 executor 方法,并立即执行它.并且 resolve和reject是 executor 中的两个参数,分别对应两个方法.我们接着往下敲
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
// 默认为等待状态
status = PENDING
constructor(executor) {
...
}
resolve = () => {
// 不是pending状态说明 状态已经改变,就不会往下执行了
if(this.status!== PENDING) return;
// 执行 resolve时修改状态
this.status = FULFILLED
}
reject = () => {
// 不是pending状态说明 状态已经改变,就不会往下执行了
if(this.status !== PENDING) return;
// 执行 reject时修改状态
this.status = REJECTED;
}
}
为 MyPromise 添加 then 方法
这里我们增加了状态的概念, 首先定义了三个状态, 在类里面初始化一个状态status为pending状态,执行 resolve前判断状态是否改变,改变了就不执行,否则改变状态, reject类似.接着往下
class MyPromise {
// 默认为等待状态
status = PENDING
value = undefined
reason = undefined
constructor(executor) {
...
}
resolve = value => {
...
this.value = value
}
reject = reason => {
...
this.reason = reason
}
then(successCallback, failCallback) {
// 新增两个回调,当状态为 fullfilled,说明调用了 resolve,代表成功回调,调用 successCallback
// 我们使用 then的时候会接受到一个值,这个值是在 resolve执行之后传递过来的,failCallbac
if(this.status === FULFILLED) successCallback(this.value)
else if(this.status === REJECTED) failCallback(this.value)
}
}
这里我们增加了 then方法,调用 then的时候 then(value => console.log(value))我们可以直接获取到这个 value的值,那么这个值那里来的呢? 这个值是在 promise中调用了 resolve('成功')传递过来的,所以我在 MyPromise中定义了一个 value, 在 resolve执行的时候将 value赋值给 this.value,然后 successCallback就可以拿到值了, 这个时候我们执行一下
let promise = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失败')
})
promise.then(value => {
console.log(value) // 成功
}, reason => {
console.log(reason)
})
就会发现 已经能够打印出成功,至此, promise核心逻辑完了,是不是觉得挺 easy 😎.
上面的完整代码
详细代码点击我展开 👈
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
// 默认为等待状态
status = PENDING
value = undefined
reason = undefined
constructor(executor) {
// new Mypromise会立即执行 executor
// executor 中有两个方法分别是 resolve, reject
executor(this.resolve, this.reject)
}
resolve = value => {
// 不是pending状态说明 状态已经改变,就不会往下执行了
if(this.status!== PENDING) return;
// 执行 resolve 时修改状态
this.status = FULFILLED;
this.value = value
}
reject = reason => {
// 不是pending状态说明 状态已经改变,就不会往下执行了
if(this.status !== PENDING) return;
// 执行 reject 时修改状态
this.status = REJECTED;
this.reason = reason
}
then(successCallback, failCallback) {
// 新增两个回调,当状态为 fullfilled,说明调用了 resolve,代表成功回调,调用 successCallback
// 我们使用 then的时候会接受到一个值,这个值是在 resolve执行之后传递过来的,failCallback类似
if(this.status === FULFILLED) successCallback(this.value)
else if(this.status === REJECTED) failCallback(this.reason)
}
}
添加异步逻辑
先看示例代码
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 200);
})
promise.then(value => {
console.log(value) // 什么也没有打印
}, reason => {
console.log(reason)
})
当我们给 resolve 加上一个异步时就会发现 console 什么也没有打印,这是什么原因呢?
if(this.status === FULFILLED) successCallback(this.value)
else if(this.status === REJECTED) failCallback(this.value)
new Promise 这个动作执行完成之后就会立即执行 promise.then,而这个时候 状态其实还是pending,而又因为我们在 then 源码里面加了判断条件状态为 fullfilled 或者 rejected 时才会执行回调,所以现在什么也没有执行.
接下来我们就来实现异步的操作
class MyPromise {
...
successCallback = undefined
failCallback = undefined
constructor(executor) {
...
}
resolve = value => {
...
this.successCallback && this.successCallback(value)
}
reject = reason => {
...
this.failCallback && this.failCallback(reason)
}
then(successCallback, failCallback) {
...
else {
// 当 status 为pending时 保存 successCallback和 failCallback,当要执行 resolve时,我们再到resolve执行successCallback
this.successCallback = successCallback
this.failCallback = failCallback
}
}
}
首先我们定义了两个回调函数
我们在 then 里面增加一个 else 语句用来判断当状态为 pending 时, 我们先保存两个回调函数
当异步执行 resolve 的时候我们再去调用回调函数.所以在 resolve中加了一行代码,首先判断this.successCallback是否有,没有代表 resolve没有异步操作,也就不会执行,有就会执行并把 value穿给回调, 失败回调类似,就不赘述了.
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 200);
})
promise.then(value => {
console.log(value) // 成功
}, reason => {
console.log(reason)
})
然后我们再打印就会发现可以正常输出啦
上述完整代码如下
详细代码点击我展开 👈
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
// 默认为等待状态
status = PENDING
value = undefined
reason = undefined
constructor(executor) {
// new Mypromise会立即执行 executor
// executor 中有两个方法分别是 resolve, reject
executor(this.resolve, this.reject)
}
resolve = value => {
// 不是pending状态说明 状态已经改变,就不会往下执行了
if(this.status!== PENDING) return;
// 执行 resolve 时修改状态
this.status = FULFILLED;
this.value = value
}
reject = reason => {
// 不是pending状态说明 状态已经改变,就不会往下执行了
if(this.status !== PENDING) return;
// 执行 reject 时修改状态
this.status = REJECTED;
this.reason = reason
}
then(successCallback, failCallback) {
// 新增两个回调,当状态为 fullfilled,说明调用了 resolve,代表成功回调,调用 successCallback
// 我们使用 then的时候会接受到一个值,这个值是在 resolve执行之后传递过来的,failCallback类似
if(this.status === FULFILLED) successCallback(this.value)
else if(this.status === REJECTED) failCallback(this.reason)
}
}
实现then方法多次调用(不是链式调用)
再看一个示例
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 200);
})
promise.then(value => {
console.log(value) // 什么也没有输出
}, reason => {
console.log(reason)
})
promise.then(value => {
console.log(value) // 输出成功
}, reason => {
console.log(reason)
})
这里解释起来稍微有那么一丢丢复杂,慢慢来,首先第一次执行 promise.then 时 resolve 还未执行,这个时候 其实 代码会接着往下执行,也就是执行下一个promise.then
源码中第一次执行 promise.then的时候做了什么事呢? 就是将 successcallback 保存了起来,那么其实就会把上一次的 successcallback 覆盖掉了. 只会执行最后一个 then 了,不知道这么说有没有看懂呢???
所以接下来我们就来解决这个问题,上代码
class MyPromise {
// successCallback = undefined
// failCallback = undefined
// 修改 successCallback 和 failCallback 为数组
successCallback = []
failCallback = []
constructor(executor) {
...
}
resolve = value => {
...
// this.successCallback && this.successCallback(value)
while(this.successCallback.length) this.successCallback.shift()(value)
}
reject = reason => {
...
// this.failCallback && this.failCallback(reason)
while(this.failCallback.length) this.failCallback.shift()(reason)
}
then(successCallback, failCallback) {
...
else {
// 当 status 为pending时 保存 successCallback和 failCallback,当要执行 resolve时,我们再到resolve执行successCallback
// this.successCallback = successCallback
// this.failCallback = failCallback
// 将一个个的callback推入数组
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
}
}
简单来说就是 将 回调函数 通过数组的形式保存, 每次通过 原对象.then 都会push一个回调(注意只有异步resolve才会push,可以思考下为什么同步就不需要push呢) 这个时候再执行上面的案例就发现两个promise都成功执行了
详细代码点击我展开 👈
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
// 默认为等待状态
status = PENDING
value = undefined
reason = undefined
// successCallback = undefined
// failCallback = undefined
// 修改 successCallback 和 failCallback 为数组
successCallback = []
failCallback = []
constructor(executor) {
// new Mypromise会立即执行 executor
// executor 中有两个方法分别是 resolve, reject
executor(this.resolve, this.reject)
}
resolve = value => {
// 不是pending状态说明 状态已经改变,就不会往下执行了
if(this.status!== PENDING) return;
// 执行 resolve 时修改状态
this.status = FULFILLED;
this.value = value
// this.successCallback && this.successCallback(value)
while(this.successCallback.length) this.successCallback.shift()(value)
}
reject = reason => {
// 不是pending状态说明 状态已经改变,就不会往下执行了
if(this.status !== PENDING) return;
// 执行 reject 时修改状态
this.status = REJECTED;
this.reason = reason
// this.failCallback && this.failCallback(reason)
while(this.failCallback.length) this.failCallback.shift()(reason)
}
then(successCallback, failCallback) {
// 新增两个回调,当状态为 fullfilled,说明调用了 resolve,代表成功回调,调用 successCallback
// 我们使用 then的时候会接受到一个值,这个值是在 resolve执行之后传递过来的,failCallback类似
if(this.status === FULFILLED) successCallback(this.value)
else if(this.status === REJECTED) failCallback(this.reason)
else {
// 当 status 为pending时 保存 successCallback和 failCallback,当要执行 resolve时,我们再到resolve执行successCallback
// this.successCallback = successCallback
// this.failCallback = failCallback
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
}
}
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 200);})
promise.then(value => {
console.log(value) // 成功
}, reason => {
console.log(reason)
})
promise.then(value => {
console.log(value) // 成功
}, reason => {
console.log(reason)
})
实现then的链式调用
现在文章篇幅过长,放到下一篇, 这里在我看来是实现promise最难的地方