Promise是前端异步回调的一个解决方案,更多的介绍其实网上都有。但是Promise规范有很多,如Promise/A,Promise/B,Promise/D 以及 Promise/A 的升级版Promise/A+。
0x00 前言
在阅读规范后,现在简单按照 Promise/A+ 的规范手撸一个简单的Promise对象。主要是为了加深认识Promise的规范和理解使用。
【翻译】Promises/A+规范
0x01 条件
首先我们需要了解一个简单的Promise至少需要满足什么条件。
- Promise 的实例是具有状态的,而状态包括:等待态(Pending)、执行态(Fulfilled)、拒绝态(Rejected)。
- 一个 Promise 必须提供一个 then 方法以访问其当前值、终值和据因。
0x02 实现
02.1 定义
首先我们通过一个立即执行匿名函数构建一个封闭的作用域,避免污染问题。
使用Promise
的时候,我们都需要去创建一个新的实例,如:new Promise(...)
,所以我们需要最后返回的是一个Function
。
简单延伸一下,new
做了什么操作:在Javascript中new是作为一个保留的关键词存在,其中new的操作是隐式的新建一个临时的空白对象,并拷贝了function.prototype
的属性,最后将构造函数中的this指向这个临时新建的对象,最后返回这个临时对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
|
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); } } ); }); }
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));
这段代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| 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 完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
| 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
的函数怎么实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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; })();
|