0x00 前言
Promise是前端异步回调的一个解决方案,更多的介绍其实网上都有。但是Promise规范有很多,如Promise/A,Promise/B,Promise/D 以及 Promise/A 的升级版Promise/A+。
现在简单按照 Promise/A+ 的规范手撸一个简单的Promise对象。
0x01 条件
首先我们需要了解一个简单的Promise至少需要满足什么条件。
- Promise 的实例是具有状态的,而状态包括:等待态(Pending)、执行态(Fulfilled)、拒绝态(Rejected)。
- 一个 Promise 必须提供一个 then 方法以访问其当前值、终值和据因。
0x02 实现
02.1 定义
首先我们通过一个立即执行匿名函数构建一个封闭的作用域,避免污染问题。
使用Promise的时候,我们都需要去创建一个新的实例,如:new Promise(...),所以我们需要最后返回的是一个Function。
简单延伸一下,new做了什么操作:在Javascript中new是作为一个保留的关键词存在,其中new的操作是隐式的新建一个临时的空白对象,并拷贝了function.prototype的属性,最后将构造函数中的this指向这个临时新建的对象,最后返回这个临时对象
var PromiseA = (() => {
const PENDING = 0; //等待态
const FULFILLED = 1; //执行态
const REJECTED = 2; //拒绝态
function PromiseA(fn) {
this.state = PENDING;
this.handlers = [];
fn(this._resolve.bind(this), this._reject.bind(this));
}
Object.assign(PromiseA.prototype,{...}) // 后续逐个解释
return PromiseA;
})();
用于现在大部分浏览器都已经默认支持Promise对象,为了避免冲突所以另起PromiseA作为例子。
构造函数中默认赋值 this.state = PENDING,当实例新建的时候实例即处于等待状态。
由于规范中要求:then 方法可以被同一个 promise 调用多次。所以 this.handlers = [],用于在实例还处于PENDING状态时将多个 then 方法收集起来。
fn(this.resolve.bind(this), this.reject.bind(this)) 立即调用创建实例时传入的函数方法,并且将 resolve\reject 方法绑定当前 promise 实例后以参数形式传递。
到这里Promise的构造函数就写完了,接下来我们需要根据规范完成其他都应的方法。
02.2 promise.then
- promise 的 then 方法接受两个参数:
promise.then(onFulfilled, onRejected) - then 方法必须返回一个 promise 对象
/**
* promise 的 then 方法
**/
PromiseA.prototype.then(onFulfilled, onRejected) {
return new PromiseA((resolve, reject) => {
this._registerHandler(
result => {
try {
if (onFulfilled && onFulfilled instanceof Function) {
resolve(onFulfilled(result));
} else {
resolve(result);
}
} catch (err) {
reject(err);
}
},
reason => {
try {
if (onRejected && onRejected instanceof Function) {
resolve(onRejected(reason));
} else {
reject(reason);
}
} catch (err) {
reject(err);
}
}
);
});
}
/**
* 注册then的回调方法,如果是PENDING状态则添加到队列。
* 如果是FULFILLED 、 REJECTED 则直接执行方法
**/
PromiseA.prototype._registerHandler(onFulfilled, onRejected) {
if (this.state === PENDING) {
this.handlers.push({
onFulfilled,
onRejected
});
} else {
setTimeout(() => {
if (this.state === FULFILLED) {
onFulfilled(this.result);
} else {
onRejected(this.reason);
}
}, 0);
}
}
根据上面的两个条件,我们知道每个then方法的返回值是 promise 对象,所以直接创建一个新的 Promise。
this._registerHandler 这段代码中由于使用了箭头函数,所以 this 指向的是调用了 then 方法的 promise 实例,而非新创建的返回实例。this._registerHandler 将根据当前的 promise 实例状态判断执行不断的操作,如果当前状态是PENDING则将回调方法暂时存放在 handlers 中,如果是FULFILLED或者REJECTED则直接执行其中对应的回调。
this._registerHandler接受的第一个参数是成功后的回调方法,第二个则是失败的。
如果 onFulfilled 不是函数,其必须被忽略 如果 onRejected 不是函数,其必须被忽略
根据规则,如果判断这两个参数不是函数,则直接跳过处理。但有个地方需要注意的是,经过promise.then#onRejected的方法处理后如果没有抛出新的错误或者返回一个新的Promise.reject,接下来的状态则应该是FULFILLED。
如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)
最后要说的是为什么setTimtou(,0),因为无论是静态值还是回调返回的值,当执行resolve后,then的回调方法都会放在微任务(mircoTask)队列中等待执行。所以这里简单用setTimeout模拟了。
02.3 _resolve _reject
经过上面的代码热身大家应该能有一点感觉了,但其最核心主要的还是构造函数中的fn(this._resolve.bind(this), this._reject.bind(this));这段代码。
PromiseA.prototype._resolve(result) {
let then = result && this._getThen(result);
if (then) {
try {
then.call(
result,
thenResult => {
if (this.state !== PENDING) return;
this._fulfill(thenResult);
},
thenReason => {
if (this.state !== PENDING) return;
this._reject(thenReason);
}
);
} catch (e) {
if (this.state !== PENDING) return;
this._reject(e);
debugger;
console.error(e);
}
} else {
this._fulfill(result);
}
}
PromiseA.prototype._getThen(result) {
let then = result.then;
if (then && then instanceof Function) {
return then;
}
return null;
}
PromiseA.prototype._fulfill(result) {
if (this.state !== PENDING) return;
this.state = FULFILLED;
this.result = result; // 必须拥有一个不可变的终值
// 调用回调函数
this.handlers.forEach(handler => {
handler.onFulfilled(result);
});
}
PromiseA.prototype._reject(reason) {
if (this.state !== PENDING) return;
this.state = REJECTED;
this.reason = reason; //必须拥有一个不可变的据因
this.handlers.forEach(handler => {
handler.onRejected(reason);
});
}
首先我们看下规则:
x 为 Promise 如果 x 为 Promise ,则使 promise 接受 x 的状态 注4: 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝 如果 x 处于执行态,用相同的值执行 promise 如果 x 处于拒绝态,用相同的据因拒绝 promise
x指的是代码中的result
从规则中看到,我们需要在_resolve函数中判断返回值是否 Promise 对象,如果是的话需要等待这个promise直至被执行或者拒绝,这个时候我们需要对这个 promise 注册回调,当回调成功的时候则调用this._fulfill。如果不是 Promise 则直接调用 this._fulfill。
再看 _getThen,其实只是满足其中规则中定义的thenable(是一个定义了 then 方法的对象或函数。)去获取then方法。
_fulfill、_reject。判断当前实例状态是否PENDING,因为根据状态中的规则来看只有PENDING可以迁移至执行态或拒绝态,并且不可逆。
0x03 图解示例
- 创建一个新的Promise实例(promise1)时,创建的参数是一个函数,函数接收两个参数,分别是函数:resolve、reject。通过调用其中一个函数决定当前Promise实例处于何种状态。
- 当Promise实例调用then的时候,会立即创建一个新的Promise实例(thenPromise1)并返回。创建新的Promise时候,会在调用then的promise实例(promise1)中注册新的handler。
- 注册handler的时候,会判断promise实例(promise1)是否处于PENDING状态,如果是的话则将方法放入队列,否则直接执行对应状态的方法。
0x04 完整代码
var PromiseA = (global => {
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
let id = 0;
function PromiseA(fn) {
this.id = ++id;
this.state = PENDING;
this.handlers = [];
fn.call(global, this._resolve.bind(this), this._reject.bind(this));
}
Object.assign(PromiseA.prototype, {
then(onFulfilled, onRejected) {
return new PromiseA((resolve, reject) => {
this._registerHandler(
result => {
try {
if (onFulfilled && onFulfilled instanceof Function) {
resolve(onFulfilled(result));
} else {
resolve(result);
}
} catch (err) {
reject(err);
}
},
reason => {
try {
if (onRejected && onRejected instanceof Function) {
resolve(onRejected(reason));
} else {
reject(reason);
}
} catch (err) {
reject(err);
}
}
);
});
},
_resolve: function(result) {
let then = result && this._getThen(result);
if (then) {
try {
then.call(
result,
thenResult => {
if (this.state !== PENDING) return;
this._fulfill(thenResult);
},
thenReason => {
if (this.state !== PENDING) return;
this._reject(thenReason);
}
);
} catch (e) {
if (this.state !== PENDING) return;
this._reject(e);
debugger;
console.error(e);
}
} else {
this._fulfill(result);
}
},
_reject(reason) {
this._reject(reason);
},
_registerHandler(onFulfilled, onRejected) {
if (this.state === PENDING) {
this.handlers.push({
onFulfilled,
onRejected
});
} else {
setTimeout(() => {
if (this.state === FULFILLED) {
onFulfilled(this.result);
} else {
onRejected(this.reason);
}
}, 0);
}
},
_fulfill(result) {
if (this.state !== PENDING) return;
this.state = FULFILLED;
this.result = result;
this.handlers.forEach(handler => {
debugger;
handler.onFulfilled(result);
});
},
_reject(reason) {
if (this.state !== PENDING) return;
this.state = REJECTED;
this.reason = reason;
this.handlers.forEach(handler => {
handler.onRejected(reason);
});
},
_getThen(result) {
let then = result.then;
if (then && then instanceof Function) {
return then;
}
return null;
}
});
return PromiseA;
})(this);
0x05 最后
PromiseA.resolve 的实现其实也很简单,各类扩展的方法就不展开讨论。最后简单的说一下PromiseA.resolve的函数怎么实现。
var PromiseA = (() => {
function PromiseA(fn) {
this.state = PENDING;
this.handlers = [];
fn(this._resolve.bind(this), this._reject.bind(this));
}
//...
PromiseA.resolve = function(result){
return new PromiseA(resolve=>resolve(result));
}
return PromiseA;
})();