JS 实现 Promise
MDN Promise 参考地址:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
之前查阅了几篇blog作为参考,最终参考了:https://www.cnblogs.com/hjj2ldq/p/9489598.html
源码已经放在github:https://github.com/ronliruonan/sv-book/tree/master/javascript/promise
MDN语法的理解
var executor = function(resolve, reject){...}
new Promise(executor);
-
Promise构造函数 接收的一个executor函数
-
executor函数 接收两个参数(resolve, reject):resolve 被作为成功函数来对待使用,reject 被作为失败函数来对待使用;
-
resolve函数 被调用时,Promise的状态改为
fulfilled
,reject函数被调用时,Promise状态改为rejected
-
Promise 在接收到executor函数时,将会
立即执行
executor函数,并且发生在Promise返回promise实例之前被调用 -
executor执行过程中,成功逻辑调用resoleve函数;失败逻辑调用reject函数;如果发生异常,Promise的状态将是
rejected
-
executor函数的返回值将被忽略。
MDN描述的理解
-
Promsie 具有3种状态,除了上面提到的
fulfilled
和rejected
两种,还有一个种:pending
(初始状态,不代表成功,也不代表失败状态) -
Promise.prototype.then(onfulfilled, onrejected) 接收两个回调函数;最终返回promise对象,传递了原有的status
-
Promise.prototype.catch() 最终也是返回promise对象
-
综上,Promise可以被链式调用
MDN的方法
- Promise.all(iterable)
- Promise.race(iterable)
- Promise.reject(reason)
- Promise.resolve(value) value具有三种类型,需要特殊对待
MDN的原型 - 方法
- Promise.prototype.catch(onRejcted)
- Promise.prototype.then(onFulfilled, onRejected)
- Promise.prototype.finally(onFinally)
回归主题 js 实现Promise
此次仅实现了
- Promsie.prototype.constructor
- Promise.prototype.then(onResolve, onReject)
- Promise.reject(onReject)
- Promsie.resolve(value) 未实现value包含
then:function
的特性
简版实现思维
用到了队列 和 闭包。 代码实现在 js-promise.html
这个思路受到blog的启发,基于blog的思路,实现了更为贴切原生Promise的功能
function (executor) {
this.status = 'pending'; // 默认状态
this.value = void 0; // 默认值 undefined
this.keepResolveFn = []; // 成功回调队列
this.keepRejectFn = []; // 失败回调队列
const resolve = (val) => {
this.status === 'pending' && (
this.status = 'fulfilled',
this.value = val,
this.keepResolveFn.forEach(fn => fn())
);
};
const reject = (val) => {
this.status === 'pending' && (
this.status = 'rejected',
this.value = val,
this.keepRejectFn.forEach(fn => fn())
);
};
try {
executor(resolve, reject); // Promise 内部立即执行executor函数
} catch (error) {
reject(error);
}
}
FnPromise.prototype.then = function (onResolve, onReject) {
this.status === 'fulfilled' && onResolve(this.value);
this.status === 'rejected' && onReject(this.value);
// 这一步pending状态的方案,真的佩服参考blog的作者,茅塞顿开
this.status === 'pending' && (
this.keepResolveFn.push(() => onResolve(this.value)),
this.keepRejectFn.push(() => onReject(this.value))
);
return this; // 自动传递了Promise的状态
}
验证 简版
通过与原生Promise的使用对比,直接resolve
/* 最简单的成功回调 */
var p1 = new Promise(resolve => resolve(200));
p1
.then(res => console.log('p1 最简单的成功回调: ', res))
/* Promise.then()返回promise对象,并保留resolve状态 */
.then(a => console.log('p1 必须出现'), b => console.log('p1 不会出现'));
/* 最简单的成功回调 */
var fp1 = new Promise(resolve => resolve(200));
fp1
.then(res => console.log('fp1 最简单的成功回调: ', res))
/* Promise.then()返回promise对象,并保留resolve状态 */
.then(a => console.log('fp1 必须出现'), b => console.log('fp1 不会出现'));
通过与原生Promise的使用对比,使用setTimeout() 异步 resolve
/* 异步回调 */
var sync_p = new Promise(resolve => setTimeout(resolve, Math.random() * 4000, 304));
sync_p
.then(res => console.log('sync_p 异步回调: ', res))
/* 异步回调的.then() 返回同样状态的promise */
.then(a => console.log('sync_p 必须成功'), b => console.log('sync_p 不可能出现'));
/* 异步回调 */
var sync_fp = new Promise(resolve => setTimeout(resolve, Math.random() * 5000, 304));
sync_fp
.then(res => console.log('sync_fp 异步回调: ', res))
/* 异步回调的.then() 返回同样状态的promise */
.then(a => console.log('sync_fp 必须成功'), b => console.log('sync_fp 不可能出现'));
升级版本疑问
简版Promise 无法实现一下原生效果。
一大难点:异步Promise后.then()中直接return 新的异步Promise后,下一次.then()的所属为上一步的新异步Promise
var b = 10;
var p1 = new Promise(resolve => {
setTimeout(() => {
console.log(' b += 10');
b += 10;
resolve();
}, 1000 * 10);
});
p1
.then(() => {
console.log(' 第一次输出应该是20: ', b);
return 2;
})
.then((res) => {
console.log(' 第二次输出应该是20: ', b);
console.log('res is 2: ', res);
return new Promise(resolve => {
setTimeout(() => {
console.log(' b *= b')
b *= b;
resolve();
}, 1000 * 2);
});
})
.then(() => {
console.log(' 第3次输出应该是20*20: ', b);
return new Promise(resolve => {
setTimeout(() => {
console.log(' b = 0')
b = 0;
resolve();
}, 1000 * 2);
});
})
.then(
() => console.log(' 应该是最后一次输出0: ', b),
() => console.log(' 不应该出现异常')
);
升级版思维
沿用简版的队列、闭包思维,增加了递归(用来修订队列中的FnPromise的执行关系)
代码实现在 js-promise-plug.html
已经亲自验证了 升级版的疑问效果。
ps:
思考方案 -> blog论证 -> 简版(coding、代码验证、逻辑推敲、代码验证、逻辑推敲) -> 升级版(coding、代码验证、逻辑推敲、代码验证、逻辑推敲…) -> 脑仁疼了2、3天