以 Promise/A+ 做为标准,编写一个可通过标准测试的Promise类库。
什么是Promise
- 主要用于异步计算
- 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果。避免回调地域
- 可以在对象之间传递和操作promise,帮助我们处理队列
Promise类的结构
- Promise对象初始状态为 Pending,在被 resolve 或 reject 时,状态变为 Fulfilled 或 Rejected
- resolve接收成功的数据,reject接收失败或错误的数据
- Promise对象必须有一个 then 方法,且只接受两个可函数参数 onFulfilled、onRejected
const REJECTED = 'rejected';
const RESOLVED = 'resolved';
const PENDING = 'pending';
class MPromise{
constructor(resolver){
if(resolver && typeof resolver !== 'function'){
throw new Error('MPromise resolver is not function');
}
this.state = PENDING; //当前promise对象的状态
this.data = undefined; //当前promise对象的数据(成功或失败)
this.callbackQueue = []; //当前promise对象注册的回调队列
if(resolver){
executeResolver.call(this, resolver);
}
}
then(){
//todo
}
}
所以,一个 Promise构造函数 和一个实例方法then 就是Promise的核心的了,其它的都是Promise的语法糖或者说是扩展
executeResolver
构造器的初始化,使用 new Promise(function(resolve, reject){...})实例化 Promise 时,去改变promise的状态,是执行 resolve() 或 reject()方法,那么,resolver的两个参数分别是成功的操作函数和失败的操作函数。
function executeResolver(resolver){
let called = false; //状态变更不可逆
let _this = this;
function onError(reason){
if(!called){
return;
}
called = true;
executeCallback.call(_this,'reject', reason);
}
function onSuccess(value){
if(!called){
return;
}
called = true;
executeCallback.call(_this, 'resolve', value);
}
try{
resolver(onSuccess, onError);
}catch(e){
onError(e);
}
}
这里将上面执行 resolver 的方法抽象出来,内部再将 resovle 和 reject 两个参数包装成 成功和失败的回调。
executeCallback
因为执行 resolve() 或 reject() 内部主要作用是更改当前实例的状态为 rejected 或 resolved,然后执行当前实例 then() 中注册的 成功或失败的回调函数, 所以从过程上来看,大致是相同的,抽象出来共用
function executeCallback(type, x){
const isResolve = type === 'resolve' ? true : false;
let thenable;
if(isResolve && typeof x === 'object' && typeof x.then === 'function'){
try {
thenable = getThen(x);
} catch (e) {
return executeCallback.call(this, 'reject', e);
}
}
if(isResolve && thenable){
executeResolver.call(this, thenable); //注意是this
} else {
this.state = isResolve ? RESOLVED : REJECTED;
this.data = x;
this.callbackQueue.forEach(v => v[type](x));
}
return this;
}
function getThen(obj){
const then = obj && obj.then;
if(obj && typeof obj === 'object' && typeof then === 'function'){
return applyThen(){
then.apply(obj, arguments)
}
}
}
then
标准中规定:
- then 方法必须返回一个新的 Promise实例(ES6中的标准,Promise/A+中没有明确说明)
- 为了保证 then中回调的执行顺序,onFulfilled 或 onRejected 必须异步调用
class MPromise{
...
then(onResolved, onRejected){
//回调不是函数,可以忽略
if(this.state === RESOLVED && onResolved !== 'function'
|| this.state === REJECTED && onRejected !== 'function'){
return this;
}
let promise = new MPromise();
if(this.state !== PENDING){
var callback = this.state === RESOLVED ? onResolved : onRejected;
//注意:传入promise,
//异步调用
executeCallbackAsync.call(promise, callback, this.data);
} else {
this.callbackQueue.push(new CallbackItem(promise, onResolved, onRejected))
}
return promise; //必须返回promise,才能链式调用
}
}
executeCallbackAsync
上面将异步调用callback的逻辑抽象成了一个方法executeCallbackAsync ,这个方法主要功能是安全的执行callback方法:
- 如果出错,则自动调用 reject(reason) 方法并更改状态为 rejected,传递错误数据给当前实例then方法中注册的onRejected 回调
- 如果成功,则自动调用 resolve(value)方法并更改状态为resolved,传递数据给当前实例then方法中注册的 onResolved 回调
function executeCallbackAsync(callback, value){
let _this = this;
setTimeout(() => {
let res;
try{
res = callback(value);
}catch(e){
return executeCallback.call(_this, 'reject', e);
}
if(res !== _this){
return executeCallback.call(_this, 'resolve', res);
} else {
return executeCallback.call(_this, 'reject', new TypeError('Cannot resolve promise with itself'));
}
}, 4);
}
注意这里最好不要用 setTimeout ,使用 setTimeout 可以异步执行回调,但其实并不是真正的异步线程,而是利用了浏览器的 Event Loop 机制去触发执行回调,而浏览器的事件轮循时间间隔是 4ms ,所以连接的调用 setTimeout 会有 4ms 的时间间隔,而在Nodejs 中的 Event Loop 时间间隔是 1ms,所以会产生一定的延迟,如果promise链比较长,延迟就会越明显,这里可以引入NPM上的 immediate 模块来异步无延迟的执行回调。
CallbackItem
上面then中对于回调的处理,使用了一个回调对象来管理注册的回调,将回调按顺序添加至 callbackQueue 队列中,调用时,依次调用。
class CallbackItem {
constructor(promise, onResolved, onRejected) {
this.promise = promise;
this.onResolved = typeof onResolved === 'function' ? onResolved : function (v) {
return v;
};
this.onRejected = typeof onRejected === 'function' ? onRejected : function (v) {
throw v;
};
}
resolve(value) {
executeCallbackAsync.call(this.promise, this.onResolved, value);
}
reject(value) {
executeCallbackAsync.call(this.promise, this.onRejected, value);
}
}
例子
以上参考深入理解Promise中的文章,实现MPromise
function fn() {
let promise1 = new MPromise((resolve, reject) => {
resolve(1);
});
new MPromise((resolve, reject) => {
resolve(promise1); //系统执行promise1.then
}).then(res => {
console.log(res);
return 222;
}).catch(err => {
console.log(err);
});
}
fn(); // 1