# 前言
嗯嗯嗯,等写完这个博客我就去复习期末。
# 实现过程
# 基本属性
对一个 promise 来说,我们需要有下面这些东西
- 对应三个状态的表示
- 当前 promise 的数据(也就是成功获得的数据或者失败的原因)
- 当前 promise 的状态
- 成功时要执行的回调函数列表
- 失败时要执行的回调函数列表
另外,为了使用属性私有化,我们要用一个立即执行函数来生成 class,然后私有属性全部使用 Symbol 类型定义
const MyPromise = (function () { | |
// 对应的三个状态 | |
const PENDING = "pending"; | |
const RESOLVED = "resolved"; | |
const REJECTED = "rejected"; | |
// 当前 promise 的数据 | |
const PromiseValue = Symbol("PromiseValue"); | |
// 当前 promise 的状态 | |
const PromiseStatus = Symbol("PromiseStatus"); | |
// 成功时要执行的回调函数列表 | |
const onFulfilledList = Symbol("onFulfilledList"); | |
// 失败时要执行的回调函数列表 | |
const onRejectedList = Symbol("onRejectedList"); | |
return class { | |
constructor(executor) { | |
this[PromiseStatus] = PENDING; | |
this[PromiseValue] = undefined; | |
this[onFulfilledList] = []; | |
this[onRejectedList] = []; | |
} | |
} | |
})(); |
可以看到我们把状态,数据,两个回调列表存到了 promise 上,然后我们就可以逐步实现一个 promise 了
# 完成基础版本
先看看 promise 最基础的用法
let promise = new Promise((resolve, reject) => { | |
resolve("sena"); | |
}); |
我们可以看到,在 new Promise 时,会传入一个函数,这个函数会立即执行,这个函数接收两个参数,分别是 resolve 和 reject,这两个函数是定义好的,这个函数做了下面几件事
- 改变当前 promise 的状态
- 把传入的参数作为当前 promise 的数据保存起来
- 执行对应回调列表里里的回调函数
另外要注意的是,promise 的状态是不可逆的,只能从 pending 变成 resolved 或者 rejected,不能倒回去,所以我们要先做个判断再改变状态。
因为修改状态为成功和失败的逻辑都是一致的,所以我们可以把逻辑抽象为一个私有的工具函数
const MyPromise = (function () { | |
// 上面已经有的重复代码 | |
...... | |
// 更新状态的函数 | |
const updateStatus = Symbol("updateStatus"); | |
return class { | |
/** | |
* 更新状态 | |
* @param newStatus 新的状态 | |
* @param newValue 新的 promise 数据 | |
* @param executeQueue 要执行的回调列表 | |
*/ | |
[updateStatus] (newStatus, newValue, executeQueue) { | |
// 如果状态不是 pending, 就返回 | |
if (this[PromiseStatus] !== PENDING) return; | |
// 把当前 promise 的状态改成新的状态 | |
this[PromiseStatus] = newStatus; | |
// 把当前 promise 的值更新为新的值 | |
this[PromiseValue] = newValue; | |
// 把回调列表里的函数取出来依次执行 | |
executeQueue.forEach((handler) => { | |
// 把值传给回调函数执行 | |
handler(newValue); | |
}) | |
} | |
constructor(executor) { | |
..... | |
} | |
} | |
})(); |
好了,我们现在已经定义了一个工具函数 updateStatus
,但这个工具函数还有一点不好的地方,那就是 then 绑定的回调函数都是放到微任务中异步执行的,而我们这里是同步执行,在浏览器环境下,没有办法把回调函数放到微任务中,只能放到宏任务队列中异步执行,所以我们还要再更新一下 updateStatus
函数
因为把一个回调函数推入宏任务队列也可以抽象一下,所以我们写一个把回调函数推入任务队列的工具函数
const MyPromise = (function () { | |
...... | |
// 更新状态的函数 | |
const updateStatus = Symbol("updateStatus"); | |
// 把任务添加到宏任务队列里的函数 | |
const executeAsync = Symbol("executeAsync"); | |
return class { | |
/** | |
* 把任务推入宏队列 | |
* @param handler 回调函数 | |
* @param arg 传递给回调函数的参数 | |
*/ | |
[executeAsync] (handler, ...arg) { | |
setTimeout(function () { | |
handler(...arg); | |
}, 0) | |
} | |
/** | |
* 更新状态 | |
* @param newStatus 新的状态 | |
* @param newValue promise 新的数据 | |
* @param executeQueue 要执行的回调列表 | |
*/ | |
[updateStatus] (newStatus, newValue, executeQueue) { | |
...... | |
executeQueue.forEach((handler) => { | |
// 异步执行 then 中注册的回调 | |
this[executeAsync] (handler, newValue); | |
}) | |
} | |
constructor(executor) { | |
...... | |
} | |
} | |
})(); |
然后我们在构造器中创建 resolve 和 reject 两个函数,传入 executor 即可
constructor(executor) { | |
this[PromiseStatus] = PENDING; | |
this[PromiseValue] = undefined; | |
this[onFulfilledList] = []; | |
this[onRejectedList] = []; | |
// 定义 resolve 函数 | |
const resolve = (data) => { | |
this[updateStatus](RESOLVED, data, this[onFulfilledList]); | |
}; | |
// 定义 reject 函数 | |
const reject = (reason) => { | |
this[updateStatus](REJECTED, reason, this[onRejectedList]); | |
}; | |
// 同步执行 new Promise 时传入的函数 | |
executor && executor(resolve, reject); | |
} |
这样使用时就可以接收到 resolve 和 reject 这两个函数,然后调用他们时,就可以改变 promise 的状态,然后就可以执行 then 中注册到回调列表中的函数了。
当然我们还需要做一点小收尾,因为如果传入的 executor 函数报错,会调用这个 promise 的 reject 方法,并把错误信息传入,所以我们需要加一个 try catch
constructor(executor) { | |
this[PromiseStatus] = PENDING; | |
this[PromiseValue] = undefined; | |
this[onFulfilledList] = []; | |
this[onRejectedList] = []; | |
// 定义 resolve 函数 | |
const resolve = (data) => { | |
this[updateStatus](RESOLVED, data, this[onFulfilledList]); | |
}; | |
// 定义 reject 函数 | |
const reject = (reason) => { | |
this[updateStatus](REJECTED, reason, this[onRejectedList]); | |
}; | |
try { | |
// 同步执行 new Promise 时传入的函数 | |
executor && executor(resolve, reject); | |
}catch (e) { | |
// 如果捕获到异常就调用 reject | |
reject(e); | |
} | |
} |
然后看看我们现在已经写好的所有的代码
const MyPromise = (function () { | |
// 对应的三个状态 | |
const PENDING = "pending"; | |
const RESOLVED = "resolved"; | |
const REJECTED = "rejected"; | |
// 当前 promise 的数据 | |
const PromiseValue = Symbol("PromiseValue"); | |
// 当前 promise 的状态 | |
const PromiseStatus = Symbol("PromiseStatus"); | |
// 成功时要执行的回调函数列表 | |
const onFulfilledList = Symbol("onFulfilledList"); | |
// 失败时要执行的回调函数列表 | |
const onRejectedList = Symbol("onRejectedList"); | |
// 更新状态的函数 | |
const updateStatus = Symbol("updateStatus"); | |
// 把任务添加到宏任务队列里的函数 | |
const executeAsync = Symbol("executeAsync"); | |
return class { | |
/** | |
* 把任务推入宏队列 | |
* @param handler 回调函数 | |
* @param arg 传递给回调函数的参数 | |
*/ | |
[executeAsync] (handler, ...arg) { | |
setTimeout(function () { | |
handler(...arg); | |
}, 0) | |
} | |
/** | |
* 更新状态 | |
* @param newStatus 新的状态 | |
* @param newValue promise 新的数据 | |
* @param executeQueue 要执行的回调列表 | |
*/ | |
[updateStatus] (newStatus, newValue, executeQueue) { | |
// 如果状态不是 pending, 就返回 | |
if (this[PromiseStatus] !== PENDING) return; | |
// 把当前 promise 的状态改成新的状态 | |
this[PromiseStatus] = newStatus; | |
// 把当前 promise 的值更新为新的值 | |
this[PromiseValue] = newValue; | |
// 把回调列表里的函数取出来依次执行 | |
executeQueue.forEach((handler) => { | |
// 异步执行 then 中注册的回调 | |
this[executeAsync] (handler, newValue); | |
}) | |
} | |
constructor(executor) { | |
this[PromiseStatus] = PENDING; | |
this[PromiseValue] = undefined; | |
this[onFulfilledList] = []; | |
this[onRejectedList] = []; | |
// 定义 resolve 函数 | |
const resolve = (data) => { | |
this[updateStatus](RESOLVED, data, this[onFulfilledList]); | |
}; | |
// 定义 reject 函数 | |
const reject = (reason) => { | |
this[updateStatus](REJECTED, reason, this[onRejectedList]); | |
}; | |
try { | |
// 同步执行 new Promise 时传入的函数 | |
executor && executor(resolve, reject); | |
}catch (e) { | |
// 如果捕获到异常就调用 reject | |
reject(e); | |
} | |
} | |
} | |
})(); |
测试一下
let promise = new MyPromise((resolve, reject) => { | |
resolve("sena"); | |
}); |
自然是没有反应的,因为没有 then,没有注册回调函数,自然什么也不会发生
# 实现 then
实现 then 也不难,根据规范,我们需要返回一个 promise,因为返回的新 promise 和现有的 promise 需要关联,这里我们用一个新的工具函数来创建这个 promise
const MyPromise = (function () { | |
...... | |
// 创建串联的 promise | |
const createLinkPromise = Symbol("createLinkPromise"); | |
return class { | |
...... | |
/** | |
* 用于创建 then 返回的新 promise | |
* @param onFulfilled then 里传入的 onFulfilled 函数 | |
* @param onRejected then 里传入的 onRejected 函数 | |
*/ | |
[createLinkPromise] (onFulfilled, onRejected) { | |
return new MyPromise((resolve, reject) => { | |
}); | |
} | |
then(onFulfilled, onRejected) { | |
return this[createLinkPromise](onFulfilled, onRejected); | |
} | |
constructor(executor) { | |
...... | |
} | |
} | |
})(); |
这里我们使用了一个新的工具函数 createLinkPromise
,用于返回一个新的 promise,在这里我们会对 then 的绑定的回调函数进行处理。
# 实现单个 then 的绑定事件
then 如果传入了回调函数,首先会判断当前 promise 的状态,如果已经是 resolve 或者 reject 状态,就把回调函数推入微任务队列中异步执行,如果还是 pending 状态,就把回调函数放到 promise 的回调函数列表中,等状态改变了再执行,大概思路就是这样,我们现在来实现一下。
因为判断状态,立即执行,加入队列都可以抽象,所以我们用一个工具函数来完成这个过程
const MyPromise = (function () { | |
...... | |
// 处理 then 中传入的回调 | |
const settleHandle = Symbol("settleHandle"); | |
return class { | |
...... | |
/** | |
* 处理 then 传入的回调函数 | |
* @param handler 回调函数 | |
* @param immediatelyStatus 立即推入任务队列执行的状态 | |
* @param queue 如果不立即执行,保存的队列 | |
*/ | |
[settleHandle] (handler, immediatelyStatus, queue) { | |
// 如果传入的不是一个函数就返回 | |
if (typeof handler !== "function") return; | |
// 判断是不是立刻执行的状态 | |
if (this[PromiseStatus] === immediatelyStatus) { | |
// 是就立刻推入异步执行队列 | |
this[executeAsync] (handler, this[PromiseValue]); | |
} else { | |
// 不是就放到回调列表中 | |
queue.push(handler); | |
} | |
} | |
/** | |
* 用于创建 then 返回的新 promise | |
* @param onFulfilled then 里传入的 onFulfilled 函数 | |
* @param onRejected then 里传入的 onRejected 函数 | |
*/ | |
[createLinkPromise] (onFulfilled, onRejected) { | |
return new MyPromise((resolve, reject) => { | |
// 在这里调用 [settleHandle] 就处理 then 传入的回调了 | |
this[settleHandle](resolve, RESOLVED, this[onFulfilledList]); | |
this[settleHandle](reject, REJECTED, this[onRejectedList]); | |
}); | |
} | |
then(onFulfilled, onRejected) { | |
return this[createLinkPromise](onFulfilled, onRejected); | |
} | |
constructor(executor) { | |
...... | |
} | |
} | |
})(); |
settleHandle
就是用来处理回调函数的工具函数。
好了,现在我们可以给 promise 通过 then 注册回调了,来看看完整的代码吧
const MyPromise = (function () { | |
// 对应的三个状态 | |
const PENDING = "pending"; | |
const RESOLVED = "resolved"; | |
const REJECTED = "rejected"; | |
// 当前 promise 的数据 | |
const PromiseValue = Symbol("PromiseValue"); | |
// 当前 promise 的状态 | |
const PromiseStatus = Symbol("PromiseStatus"); | |
// 成功时要执行的回调函数列表 | |
const onFulfilledList = Symbol("onFulfilledList"); | |
// 失败时要执行的回调函数列表 | |
const onRejectedList = Symbol("onRejectedList"); | |
// 更新状态的函数 | |
const updateStatus = Symbol("updateStatus"); | |
// 把回调函数添加到任务队列里异步执行的函数 | |
const executeAsync = Symbol("executeAsync"); | |
// 创建串联的 promise | |
const createLinkPromise = Symbol("createLinkPromise"); | |
// 后续处理的通用函数 | |
const settleHandle = Symbol("settleHandle"); | |
return class { | |
/** | |
* 让回调函数异步执行 | |
* @param handler 回调函数 | |
* @param arg 传递给回调函数的参数 | |
*/ | |
[executeAsync] (handler, ...arg) { | |
setTimeout(function () { | |
handler(...arg); | |
}, 0) | |
} | |
/** | |
* 更新状态 | |
* @param newStatus 新的状态 | |
* @param newValue promise 新的数据 | |
* @param executeQueue 要执行的回调列表 | |
*/ | |
[updateStatus] (newStatus, newValue, executeQueue) { | |
// 如果状态不是 pending, 就返回 | |
if (this[PromiseStatus] !== PENDING) return; | |
// 把当前 promise 的状态改成新的状态 | |
this[PromiseStatus] = newStatus; | |
// 把当前 promise 的值更新为新的值 | |
this[PromiseValue] = newValue; | |
// 把回调列表里的函数取出来依次执行 | |
executeQueue.forEach((handler) => { | |
// 异步执行 then 中注册的回调 | |
this[executeAsync] (handler, newValue); | |
}) | |
} | |
/** | |
* 处理 then 传入的回调函数 | |
* @param handler 回调函数 | |
* @param immediatelyStatus 立即推入任务队列执行的状态 | |
* @param queue 如果不立即执行,保存的队列 | |
*/ | |
[settleHandle] (handler, immediatelyStatus, queue) { | |
// 如果传入的不是一个函数就返回 | |
if (typeof handler !== "function") return; | |
// 判断是不是立刻执行的状态 | |
if (this[PromiseStatus] === immediatelyStatus) { | |
// 是就立刻推入异步执行队列 | |
this[executeAsync] (handler, this[PromiseValue]); | |
} else { | |
// 不是就放到回调列表中 | |
queue.push(handler); | |
} | |
} | |
/** | |
* 用于创建 then 返回的新 promise | |
* @param onFulfilled then 里传入的 onFulfilled 函数 | |
* @param onRejected then 里传入的 onRejected 函数 | |
*/ | |
[createLinkPromise] (onFulfilled, onRejected) { | |
return new MyPromise((resolve, reject) => { | |
// 在这里调用 [settleHandle] 就处理 then 传入的回调了 | |
this[settleHandle](onFulfilled, RESOLVED, this[onFulfilledList]); | |
this[settleHandle](onRejected, REJECTED, this[onRejectedList]); | |
}); | |
} | |
then(onFulfilled, onRejected) { | |
return this[createLinkPromise](onFulfilled, onRejected); | |
} | |
constructor(executor) { | |
this[PromiseStatus] = PENDING; | |
this[PromiseValue] = undefined; | |
this[onFulfilledList] = []; | |
this[onRejectedList] = []; | |
// 定义 resolve 函数 | |
const resolve = (data) => { | |
this[updateStatus](RESOLVED, data, this[onFulfilledList]); | |
}; | |
// 定义 reject 函数 | |
const reject = (reason) => { | |
this[updateStatus](REJECTED, reason, this[onRejectedList]); | |
}; | |
try { | |
// 同步执行 new Promise 时传入的函数 | |
executor && executor(resolve, reject); | |
}catch (e) { | |
// 如果捕获到异常就调用 reject | |
reject(e); | |
} | |
} | |
} | |
})(); |
测试一下
let promise = new MyPromise((resolve, reject) => { | |
setTimeout(function () { | |
resolve("sena"); | |
}, 1000); | |
console.log("123"); // 输出 123 | |
}); | |
promise.then(function (data) { | |
console.log(1, data); // 1s 后输出 1 "sena" | |
}); | |
promise.then(function (data) { | |
console.log(2, data); // 1s 后输出 2 "sena" | |
}); | |
promise.then(function (data) { | |
console.log(3, data); // 1s 后输出 3 "sena" | |
}); |
# 实现 then 的链式调用
因为 then 已经返回了一个 promise,所以我们要做的事情比较简单,就是处理一下回调函数的返回值和处理回调函数抛出的 error 就就行了,我们先处理返回值,返回值有两种情况
下文的的下一个 Promise 代指 createLinkPromise
返回的 promise
- 返回非 promise,直接把返回值通过调用
resolve()
传给下一个 promise,下一个 promise 把这个返回值传给 then 注册的回调函数,就实现了前一个 then 的返回值作为下个 then 的参数 - 返回 promise,给返回的 promise 用 then 注册成功和失败的回调,在两个回调中分别调用能修改下一个 promise 的状态的 resolve 和 reject 方法,从而完成数据的传递和延时执行
我们看看之前写的代码
[createLinkPromise] (onFulfilled, onRejected) { | |
return new MyPromise((resolve, reject) => { | |
// 在这里调用 [settleHandle] 就处理 then 传入的回调了 | |
this[settleHandle](onFulfilled, RESOLVED, this[onFulfilledList]); | |
this[settleHandle](onRejected, REJECTED, this[onRejectedList]); | |
}); | |
} |
[settleHandle] (handler, immediatelyStatus, queue) { | |
// 如果传入的不是一个函数就返回 | |
if (typeof handler !== "function") return; | |
// 判断是不是立刻执行的状态 | |
if (this[PromiseStatus] === immediatelyStatus) { | |
// 是就立刻推入异步执行队列 | |
this[executeAsync] (handler, this[PromiseValue]); | |
} else { | |
// 不是就放到回调列表中 | |
queue.push(handler); | |
} | |
} |
很明显,因为我们不知道当前 promise 的状态,所以我们不知道传入的回调函数声明时候执行,所以要接收返回值,就要做一些特殊处理。
处理的方法也很简单,在回调函数的外面包一层,在外层函数里接收回调函数的返回值就行了
[createLinkPromise] (onFulfilled, onRejected) { | |
return new MyPromise((resolve, reject) => { | |
// 在这里调用 [settleHandle] 就能处理 then 传入的回调了 | |
// 在外面套一层 | |
this[settleHandle]((data) => { | |
// 在这里就可以接收返回值,对返回值进行处理 | |
let result = onFulfilled(data); | |
// 把返回值传给下个 promise | |
resolve(result); | |
}, RESOLVED, this[onFulfilledList]); | |
this[settleHandle]((err) => { | |
let result = onRejected(err); | |
resolve(result); | |
}, REJECTED, this[onRejectedList]); | |
}); | |
} |
根据规范,如果 then 传入的 onFulfilled 函数执行时抛出了错误,就会调用 reject 函数,所以我们加个 try catch 在外面,由因为加 try catch 是重复的逻辑,所以我们可以再抽成一个工具函数 execute
。修改后的代码是这样的
const MyPromise = (function () { | |
...... | |
// 执行 then 绑定的回调,同时 try catch | |
const execute = Symbol("execute"); | |
return class { | |
...... | |
/** | |
* 执行回调,获取返回值,如果报错就执行 reject | |
* @param data 传给 handler 的数据 | |
* @param handler 要在 try catch 中执行的回调函数 | |
* @param resolve 下个 promise 的 resolve | |
* @param reject 下个 promise 的 reject | |
*/ | |
[execute] (data, handler, resolve, reject) { | |
try { | |
const result = handler(data); | |
resolve(result); | |
}catch (e) { | |
reject(e); | |
} | |
} | |
/** | |
* 用于创建 then 返回的新 promise | |
* @param onFulfilled then 里传入的 onFulfilled 函数 | |
* @param onRejected then 里传入的 onRejected 函数 | |
*/ | |
[createLinkPromise] (onFulfilled, onRejected) { | |
return new MyPromise((resolve, reject) => { | |
// 在这里调用 [settleHandle] 就处理 then 传入的回调了 | |
this[settleHandle]((data) => { | |
this[execute](data, onFulfilled, resolve, reject); | |
}, RESOLVED, this[onFulfilledList]); | |
this[settleHandle]((err) => { | |
this[execute](err, onRejected, resolve, reject); | |
}, REJECTED, this[onRejectedList]); | |
}); | |
} | |
constructor(executor) { | |
...... | |
} | |
} | |
})(); |
现在的代码是这样的
const MyPromise = (function () { | |
// 对应的三个状态 | |
const PENDING = "pending"; | |
const RESOLVED = "resolved"; | |
const REJECTED = "rejected"; | |
// 当前 promise 的数据 | |
const PromiseValue = Symbol("PromiseValue"); | |
// 当前 promise 的状态 | |
const PromiseStatus = Symbol("PromiseStatus"); | |
// 成功时要执行的回调函数列表 | |
const onFulfilledList = Symbol("onFulfilledList"); | |
// 失败时要执行的回调函数列表 | |
const onRejectedList = Symbol("onRejectedList"); | |
// 更新状态的函数 | |
const updateStatus = Symbol("updateStatus"); | |
// 把任务添加到任务队列里异步执行的函数 | |
const executeAsync = Symbol("executeAsync"); | |
// 创建串联的 promise | |
const createLinkPromise = Symbol("createLinkPromise"); | |
// 后续处理的通用函数 | |
const settleHandle = Symbol("settleHandle"); | |
// 执行 then 绑定的回调,同时 try catch | |
const execute = Symbol("execute"); | |
return class { | |
/** | |
* 异步执行回调函数 | |
* @param handler 回调函数 | |
* @param arg 传递给回调函数的参数 | |
*/ | |
[executeAsync] (handler, ...arg) { | |
setTimeout(function () { | |
handler(...arg); | |
}, 0) | |
} | |
/** | |
* 更新状态 | |
* @param newStatus 新的状态 | |
* @param newValue promise 新的数据 | |
* @param executeQueue 要执行的回调列表 | |
*/ | |
[updateStatus] (newStatus, newValue, executeQueue) { | |
// 如果状态不是 pending, 就返回 | |
if (this[PromiseStatus] !== PENDING) return; | |
// 把当前 promise 的状态改成新的状态 | |
this[PromiseStatus] = newStatus; | |
// 把当前 promise 的值更新为新的值 | |
this[PromiseValue] = newValue; | |
// 把回调列表里的函数取出来依次执行 | |
executeQueue.forEach((handler) => { | |
// 异步执行 then 中注册的回调 | |
this[executeAsync] (handler, newValue); | |
}) | |
} | |
/** | |
* 处理 then 传入的回调函数 | |
* @param handler 回调函数 | |
* @param immediatelyStatus 立即推入任务队列执行的状态 | |
* @param queue 如果不立即执行,保存的队列 | |
*/ | |
[settleHandle] (handler, immediatelyStatus, queue) { | |
// 如果传入的不是一个函数就返回 | |
if (typeof handler !== "function") return; | |
// 判断是不是立刻执行的状态 | |
if (this[PromiseStatus] === immediatelyStatus) { | |
// 是就立刻推入异步执行队列 | |
this[executeAsync] (handler, this[PromiseValue]); | |
} else { | |
// 不是就放到回调列表中 | |
queue.push(handler); | |
} | |
} | |
/** | |
* 执行回调,获取返回值,如果报错就执行 reject | |
* @param data 传给 handler 的数据 | |
* @param handler 要在 try catch 中执行的回调函数 | |
* @param resolve 下个 promise 的 resolve | |
* @param reject 下个 promise 的 reject | |
*/ | |
[execute] (data, handler, resolve, reject) { | |
try { | |
const result = handler(data); | |
// 如果返回值是 promise | |
if (result instanceof MyPromise) { | |
// 用 then 注册回调 | |
result.then((data) => { | |
// 把回调收到的数据转发给下个 promise | |
resolve(data); | |
}, (err) => { | |
reject(err); | |
}) | |
}else { | |
// 如果返回值不是 promise,直接传递就行 | |
resolve(result); | |
} | |
}catch (e) { | |
reject(e); | |
} | |
} | |
/** | |
* 用于创建 then 返回的新 promise | |
* @param onFulfilled then 里传入的 onFulfilled 函数 | |
* @param onRejected then 里传入的 onRejected 函数 | |
*/ | |
[createLinkPromise] (onFulfilled, onRejected) { | |
return new MyPromise((resolve, reject) => { | |
// 在这里调用 [settleHandle] 就能处理 then 传入的回调了 | |
this[settleHandle]((data) => { | |
this[execute](data, onFulfilled, resolve, reject); | |
}, RESOLVED, this[onFulfilledList]); | |
this[settleHandle]((err) => { | |
this[execute](err, onRejected, resolve, reject); | |
}, REJECTED, this[onRejectedList]); | |
}); | |
} | |
then(onFulfilled, onRejected) { | |
return this[createLinkPromise](onFulfilled, onRejected); | |
} | |
constructor(executor) { | |
this[PromiseStatus] = PENDING; | |
this[PromiseValue] = undefined; | |
this[onFulfilledList] = []; | |
this[onRejectedList] = []; | |
// 定义 resolve 函数 | |
const resolve = (data) => { | |
this[updateStatus](RESOLVED, data, this[onFulfilledList]); | |
}; | |
// 定义 reject 函数 | |
const reject = (reason) => { | |
this[updateStatus](REJECTED, reason, this[onRejectedList]); | |
}; | |
try { | |
// 同步执行 new Promise 时传入的函数 | |
executor && executor(resolve, reject); | |
}catch (e) { | |
// 如果捕获到异常就调用 reject | |
reject(e); | |
} | |
} | |
} | |
})(); |
好了,现在我们已经处理完了返回值不是 promise 的情况,让我们测试一下
let promise = new MyPromise((resolve, reject) => { | |
setTimeout(function () { | |
resolve(1); | |
}, 1000); | |
}).then(function (data) { | |
console.log(data); // 1s 后输出 1 | |
return data + 1; | |
}).then(function (data) { | |
console.log(data); // 1s 后输出 2 | |
return data + 1; | |
}).then(function (data) { | |
console.log(data); // 1s 后输出 3 | |
return data + 1; | |
}); |
现在我们来处理返回值是 promise 的情况,处理的方法原理也不难,给返回的 promise 注册回调,然后在回调里修改下一个 promise 的状态就行了
PS:下一个 promise 指的是 createLinkPromise
返回的 promise
这里直接修改 execute
函数就行了
/** | |
* 执行回调,获取返回值,如果报错就执行 reject | |
* @param data 传给 handler 的数据 | |
* @param handler 要在 try catch 中执行的回调函数 | |
* @param resolve 下个 promise 的 resolve | |
* @param reject 下个 promise 的 reject | |
*/ | |
[execute] (data, handler, resolve, reject) { | |
try { | |
const result = handler(data); | |
// 如果返回值是 promise | |
if (result instanceof MyPromise) { | |
// 用 then 注册回调 | |
result.then((data) => { | |
// 把回调收到的数据转发给下个 promise | |
resolve(data); | |
}, (err) => { | |
reject(err); | |
}) | |
}else { | |
// 如果返回值不是 promise,直接传递就行 | |
resolve(result); | |
} | |
}catch (e) { | |
reject(e); | |
} | |
} |
好了,返回值是 promise 的情况也处理好了,来测试一下
let promise = new MyPromise((resolve, reject) => { | |
setTimeout(function () { | |
resolve("sena"); | |
}, 1000); | |
console.log("sion"); | |
}).then((data) => { | |
console.log("1",data); | |
return data; | |
}).then((data) => { | |
console.log("2",data); | |
return new MyPromise((resolve) => { | |
setTimeout(() => { | |
resolve("sakura"); | |
}, 2000) | |
}); | |
}).then((data) => { | |
console.log("3",data); | |
return data; | |
}); | |
console.log("yyy"); |
输出情况,看起来是正常的
// sion | |
// yyy | |
// 1s 后输出: 1 sena | |
// 1s 后输出: 2 sena | |
// 2s 后输出: 3 sakura |
其实到这里,一个 promise 大致就完成了,接下来就是一些细节处理了
# 处理空 then
如果中间的某个 then 没有传入回调,虽然我没在规范中找到这种情况(可能是我看漏了),但是在我测试官方 promise 时回调函数的执行不会中断,所以我们要做些处理
处理的方法也比较简单,如果 onFulfilled, onRejected 没有传入,使用默认的 onFulfilled, onRejected 函数,把数据转发就行了
const MyPromise = (function () { | |
...... | |
// 默认的 OnFulfilled | |
const defaultOnFulfilled = function (data) {return data}; | |
// 默认的 OnRejected | |
const defaultOnRejected = function (err) {throw new Error(err)}; | |
return class { | |
// 给 onFulfilled, onRejected 默认值 | |
then(onFulfilled = defaultOnFulfilled, onRejected = defaultOnRejected) { | |
return this[createLinkPromise](onFulfilled, onRejected); | |
} | |
constructor(executor) { | |
...... | |
} | |
} | |
})(); |
测试代码
let promise = new MyPromise((resolve, reject) => { | |
setTimeout(function () { | |
resolve("sena"); | |
}, 1000); | |
console.log("sion"); | |
}).then((data) => { | |
console.log("1",data); | |
return data; | |
}).then((data) => { | |
console.log("2",data); | |
return new MyPromise((resolve) => { | |
setTimeout(() => { | |
resolve("sakura"); | |
}, 1000) | |
}); | |
}).then().then((data) => { | |
console.log("3",data); | |
return data; | |
}); | |
console.log("yyy"); |
输出情况
// sion | |
// yyy | |
// 1s 后输出: 1 sena | |
// 1s 后输出: 2 sena | |
// 2s 后输出: 3 sakura 如果没有处理空 then,不会出现这个 |
# 完善 promise 的其他函数
# catch 和 finally 函数
catch 其实是特殊的 then 函数,简单来说就是没有 onFulfilled 的 then 函数。
finally 也是特殊的 then 函数,就是无论什么状态都执行相同的回调
所以可以直接调用 then 来实现
catch(onRejected) { | |
return this.then(defaultOnFulfilled, onRejected); | |
} | |
finally(handler) { | |
return this.then(handler, handler); | |
} |
# all 函数
all 函数接收一个 promise 数组,返回一个 promise,只有传入的数据中的所有 promise 都处于 resolved 状态后,返回的 promise 才会处于 resolved 状态,有任何一个 promise 处于 reject 状态后,返回的 promise 就会处于 reject 状态
static all(promises) { | |
//promise 数组的长度 | |
let length = promises.length; | |
// 创建用于保存结果的数组 | |
let resultArr = new Array(length); | |
// 已经处于 resolved 状态的 promise 的个数 | |
let count = 0; | |
return new MyPromise((resolve, reject) => { | |
promises.map((promise, index) => { | |
// 给每个 promise 都注册回调 | |
promise.then((data) => { | |
// 处于 resolve 状态后,把 count 加 1 | |
count ++; | |
// 保存数据到数组中 | |
resultArr[index] = data; | |
// 如果全部处于 resolved 状态 | |
if (count >= length) { | |
// 就把返回的 promise 变成 resolved 状态 | |
resolve(resultArr); | |
} | |
}, (err) => { | |
// 有任一个处于 rejected 状态 | |
// 就把返回的 promise 置为 rejected 状态 | |
reject(err); | |
}) | |
}) | |
}) | |
} |
# race 函数
race 函数接收一个 promise 数组,返回一个 promise,返回的 promise 的状态会与 promise 数组中的第一个修改状态的项同步修改成相同的值
static race(promises) { | |
return new MyPromise((resolve, reject) => { | |
promises.forEach((promise) => { | |
// 任意一个修改状态后就修改返回的 promise 的状态 | |
promise.then((data) => { | |
resolve(data); | |
}, (err) => { | |
reject(err); | |
}) | |
}) | |
}); | |
} |
# resolve 函数
resolve 函数返回一个已经处于 resolved 状态的 promise,接收要传给回调函数的 data
static resolve(data) { | |
if (data instanceof MyPromise) return data; | |
return new MyPromise((resolve) => { | |
resolve(data); | |
}) | |
} |
# reject 函数
reject 函数返回一个已经处于 reject 状态的 promise,接收要传给回调函数的 data
static reject(err) { | |
if (data instanceof MyPromise) return err; | |
return new MyPromise((resolve, reject) => { | |
reject(err); | |
}) | |
} |
好了,到这里所有的代码就暂时实现完了,当然我后期会再加些方法上去,不过那是以后的事了,最后放一波完整的代码
const MyPromise = (function () { | |
// 对应的三个状态 | |
const PENDING = "pending"; | |
const RESOLVED = "resolved"; | |
const REJECTED = "rejected"; | |
// 当前 promise 的数据 | |
const PromiseValue = Symbol("PromiseValue"); | |
// 当前 promise 的状态 | |
const PromiseStatus = Symbol("PromiseStatus"); | |
// 成功时要执行的回调函数列表 | |
const onFulfilledList = Symbol("onFulfilledList"); | |
// 失败时要执行的回调函数列表 | |
const onRejectedList = Symbol("onRejectedList"); | |
// 更新状态的函数 | |
const updateStatus = Symbol("updateStatus"); | |
// 把任务添加到任务队列里异步执行的函数 | |
const executeAsync = Symbol("executeAsync"); | |
// 创建串联的 promise | |
const createLinkPromise = Symbol("createLinkPromise"); | |
// 后续处理的通用函数 | |
const settleHandle = Symbol("settleHandle"); | |
// 执行 then 绑定的回调,同时 try catch | |
const execute = Symbol("execute"); | |
// 默认的 OnFulfilled | |
const defaultOnFulfilled = function (data) {return data}; | |
// 默认的 OnRejected | |
const defaultOnRejected = function (err) {throw new Error(err)}; | |
return class { | |
/** | |
* 异步执行回调函数 | |
* @param handler 回调函数 | |
* @param arg 传递给回调函数的参数 | |
*/ | |
[executeAsync] (handler, ...arg) { | |
setTimeout(function () { | |
handler(...arg); | |
}, 0) | |
} | |
/** | |
* 更新状态 | |
* @param newStatus 新的状态 | |
* @param newValue promise 新的数据 | |
* @param executeQueue 要执行的回调列表 | |
*/ | |
[updateStatus] (newStatus, newValue, executeQueue) { | |
// 如果状态不是 pending, 就返回 | |
if (this[PromiseStatus] !== PENDING) return; | |
// 把当前 promise 的状态改成新的状态 | |
this[PromiseStatus] = newStatus; | |
// 把当前 promise 的值更新为新的值 | |
this[PromiseValue] = newValue; | |
// 把回调列表里的函数取出来依次执行 | |
executeQueue.forEach((handler) => { | |
// 异步执行 then 中注册的回调 | |
this[executeAsync] (handler, newValue); | |
}) | |
} | |
/** | |
* 处理 then 传入的回调函数 | |
* @param handler 回调函数 | |
* @param immediatelyStatus 立即推入任务队列执行的状态 | |
* @param queue 如果不立即执行,保存的队列 | |
*/ | |
[settleHandle] (handler, immediatelyStatus, queue) { | |
// 如果传入的不是一个函数就返回 | |
if (typeof handler !== "function") return; | |
// 判断是不是立刻执行的状态 | |
if (this[PromiseStatus] === immediatelyStatus) { | |
// 是就立刻推入异步执行队列 | |
this[executeAsync] (handler, this[PromiseValue]); | |
} else { | |
// 不是就放到回调列表中 | |
queue.push(handler); | |
} | |
} | |
/** | |
* 执行回调,获取返回值,如果报错就执行 reject | |
* @param data 传给 handler 的数据 | |
* @param handler 要在 try catch 中执行的回调函数 | |
* @param resolve 下个 promise 的 resolve | |
* @param reject 下个 promise 的 reject | |
*/ | |
[execute] (data, handler, resolve, reject) { | |
try { | |
const result = handler(data); | |
// 如果返回值是 promise | |
if (result instanceof MyPromise) { | |
// 用 then 注册回调 | |
result.then((data) => { | |
// 把回调收到的数据转发给下个 promise | |
resolve(data); | |
}, (err) => { | |
reject(err); | |
}) | |
}else { | |
// 如果返回值不是 promise,直接传递就行 | |
resolve(result); | |
} | |
}catch (e) { | |
reject(e); | |
} | |
} | |
/** | |
* 用于创建 then 返回的新 promise | |
* @param onFulfilled then 里传入的 onFulfilled 函数 | |
* @param onRejected then 里传入的 onRejected 函数 | |
*/ | |
[createLinkPromise] (onFulfilled, onRejected) { | |
return new MyPromise((resolve, reject) => { | |
// 在这里调用 [settleHandle] 就能处理 then 传入的回调了 | |
this[settleHandle]((data) => { | |
this[execute](data, onFulfilled, resolve, reject); | |
}, RESOLVED, this[onFulfilledList]); | |
this[settleHandle]((err) => { | |
this[execute](err, onRejected, resolve, reject); | |
}, REJECTED, this[onRejectedList]); | |
}); | |
} | |
then(onFulfilled = defaultOnFulfilled, onRejected = defaultOnRejected) { | |
return this[createLinkPromise](onFulfilled, onRejected); | |
} | |
catch(onRejected) { | |
return this.then(defaultOnFulfilled, onRejected); | |
} | |
finally(handler) { | |
return this.then(handler, handler); | |
} | |
constructor(executor) { | |
this[PromiseStatus] = PENDING; | |
this[PromiseValue] = undefined; | |
this[onFulfilledList] = []; | |
this[onRejectedList] = []; | |
// 定义 resolve 函数 | |
const resolve = (data) => { | |
this[updateStatus](RESOLVED, data, this[onFulfilledList]); | |
}; | |
// 定义 reject 函数 | |
const reject = (reason) => { | |
this[updateStatus](REJECTED, reason, this[onRejectedList]); | |
}; | |
try { | |
// 同步执行 new Promise 时传入的函数 | |
executor && executor(resolve, reject); | |
}catch (e) { | |
// 如果捕获到异常就调用 reject | |
reject(e); | |
} | |
} | |
static all(promises) { | |
//promise 数组的长度 | |
let length = promises.length; | |
// 创建用于保存结果的数组 | |
let resultArr = new Array(length); | |
// 已经处于 resolved 状态的 promise 的个数 | |
let count = 0; | |
return new MyPromise((resolve, reject) => { | |
promises.map((promise, index) => { | |
// 给每个 promise 都注册回调 | |
promise.then((data) => { | |
// 处于 resolve 状态后,把 count 加 1 | |
count ++; | |
// 保存数据到数组中 | |
resultArr[index] = data; | |
// 如果全部处于 resolved 状态 | |
if (count >= length) { | |
// 就把返回的 promise 变成 resolved 状态 | |
resolve(resultArr); | |
} | |
}, (err) => { | |
// 有任一个处于 rejected 状态 | |
// 就把返回的 promise 置为 rejected 状态 | |
reject(err); | |
}) | |
}) | |
}) | |
} | |
static race(promises) { | |
return new MyPromise((resolve, reject) => { | |
promises.forEach((promise) => { | |
// 任意一个修改状态后就修改返回的 promise 的状态 | |
promise.then((data) => { | |
resolve(data); | |
}, (err) => { | |
reject(err); | |
}) | |
}) | |
}); | |
} | |
static resolve(data) { | |
if (data instanceof MyPromise) return data; | |
return new MyPromise((resolve) => { | |
resolve(data); | |
}) | |
} | |
static reject(err) { | |
if (data instanceof MyPromise) return err; | |
return new MyPromise((resolve, reject) => { | |
reject(err); | |
}) | |
} | |
} | |
})(); |
感谢你的阅读