javascript 实现Promise.prototype.then
实现Promise.prototype.then
then
方法是Promise中最重要同时也是较为复杂的一部分逻辑,理解了then
方法的执行逻辑,后面的Promise方法就显得通俗易懂了。
那么首先我们还是先通过规范,将then
方法的行为准则列举出来:
-
promise 的
then
方法接受两个参数:promise.then(onFulfilled, onRejected);
-
如果
onFulfilled、onRejected
不是函数,其必须被忽略 -
如果
onFulfilled
是函数:- 当
promise
执行结束后其必须被调用,其第一个参数为promise
的终值 - 在
promise
执行结束前其不可被调用 - 其调用次数不可超过一次
- 当
-
如果
onRejected
是函数:- 当
promise
被拒绝执行后其必须被调用,其第一个参数为promise
的据因 - 在
promise
被拒绝执行前其不可被调用 - 其调用次数不可超过一次
- 当
-
then
方法必须返回一个promise
对象promise2 = promise1.then(onFulfilled, onRejected);
理解上面的“返回”部分非常重要,即:不论 promise1 被 reject 还是被 resolve 时 promise2 都会被 resolve,只有出现异常时才会被 rejected。
前两条规则比较容易理解,我们优先实现它们:
MyPromise.prototype.then = function (onResolved, onRejected) {
// 如果传入的参数不为函数,我们为它们规定默认行为,分别为return拿到的终值和抛出拿到的拒因
onResolved =
typeof onResolved === "function" ? onResolved : (value) => value;
onRejected =
typeof onRejected === "function" ? onRejected : (reason) => { throw reason; };
};
第三和第四条规定了Promise状态的更新以及回调函数的执行时机,相对容易理解,所以我们接着向下查看第五条规则:规定了then
方法的返回值为一个新的Promise。那么我们的代码就可以这样来写:
MyPromise.prototype.then = function (onResolved, onRejected) {
// ... other code
// then()返回一个新的Promise用于链式调用
return new MyPromise((resolve, reject) => {
});
};
在编写里面的逻辑前,我们还需要再整理一下思绪,既然then
方法的返回值是一个新的Promise,这不仅解释了Promise链式调用的原因,同时也带来的一个问题,这个Promise的状态如何决定?
首先,毋庸置疑的是这个Promise的返回值一定是根据then
方法其中的onResolved, onRejected
二者之一的执行结果来决定的。其次,根据onResolved, onRejected
的返回值类型,要进行不同的处理,大致分为如下三种情况:
- 如果在执行过程中出现异常,则直接变为rejected状态
- 如果成功完成执行,且返回的值不为Promise类型,则直接变为
resolved
状态,并且将返回的值作为下一个then
方法的值 - 如果成功完成执行,但返回的值为新的Promise,则由这个Promise的执行结果来决定下一个
then
方法的状态
最后,由于Promise可以在任何时刻指定回调函数,所以会有当then
方法调用时,Promise状态仍为pending
的情况,这种情况下,我们将onResolved, onRejected
添加到Promise的callbacks中,等待Promise中的异步执行完毕后,通过构造函数中相对应的方法来将回调函数推送到微队列中进行等待。
根据上面的所有规则,then
方法的最终实现如下:
MyPromise.prototype.then = function (onResolved, onRejected) {
// 如果传入的参数不为函数,我们为它们规定默认行为,分别为return拿到的终值和抛出拿到的拒因
onResolved =
typeof onResolved === "function" ? onResolved : (value) => value;
onRejected =
typeof onRejected === "function" ? onRejected : (reason) => { throw reason; };
const SELF = this;
// then()返回一个新的Promise用于链式调用
return new MyPromise((resolve, reject) => {
// 处理函数
function handler(callback) {
try {
// 执行回调函数,获取结果,并根据不同的三种结果执行相应的逻辑
let result = callback(SELF.data);
// 如果执行结果是一个新的Promise,则以该Promise的执行结果作为结果
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
// 如果执行结果是一个非Promise值,则直接将其作为终值返回
resolve(result);
}
} catch (error) {
// 如果在执行回调函数中捕获到异常,则将Promise更改为失败态,并将error作为拒因抛出
reject(error);
}
}
// 根据三种不同的Promise状态,来决定对应的逻辑
if (this.state === "pending") {
// 如果执行then()时,Promise中的执行器还未产生结果,就暂时将回调函数存储起来
this.callbacks.push({
onResolved(value) {
handler(onResolved);
},
onRejected(reason) {
handler(onRejected);
},
});
} else if (this.state === "resolved") {
Promise.resolve(null).then((e) => {
handler(onResolved);
});
} else {
// rejected同resolved原理相同,只不过调用的是onRejected()
Promise.resolve(null).then((e) => {
handler(onRejected);
});
}
});
};
注:上面代码中所使用的Promise.resolve(null).then()
用于实现将代码推送到微队列的效果,如果使用setTimeout
则只能将代码推送到宏队列,这与规范中的约定相违背。
本文地址:https://blog.csdn.net/shiyidemingzij/article/details/107194904
上一篇: 大更新!杭州米链科技UN链盟全新版本上线
下一篇: Web基础--JavaScript入门
推荐阅读
-
javascript - 根据下面数组怎么生成三级联动菜单树?(考虑性能)
-
php实现utf-8转unicode函数代码实例
-
PHP实现导出excel数据的类库用法
-
PHP如何实现链式操作的原理
-
PHP扩展类ZipArchive实现压缩解压Zip文件和文件打包下载
-
vue+echarts实现可拖动节点的折现图(支持拖动方向和上下限的设置)
-
javascript使用appendChild追加节点实例_javascript技巧
-
html页面中如何实现保留空格以及换行符的实例分析
-
【译】PHP的变量实现(给PHP开发者的PHP源码-第三部分)
-
使用PHP实现密保卡功能实现代码<打包下载直接运行>_PHP