说到promise,相信大家在日常开发中都经常使用到,它是我们异步操作中必不可少的一部分,可以让代码看起来变得更好理解;
我曾在技术社区看过许多关于promise底层原理的文章,大概原理明白,这次,我准备系统的分析实现源码并记录下来,本文将一行行代码去分析最后附加流程图和总结,希望这能对你有帮助;
promise的实现库有这么多,接下来我们以github的promise ployfill为例, 想看完整实现代码的在这里;
最基础用法
我们先想象一下promise最常见的使用方式,大概像这样
new Promise((resolve,reject) => {
setTimeout( () => resolve('result'),1000)
}).then( (value) => {
console.log( value )
})
首先是new Promise(),那么Promise肯定是一个定好的构造函数,果然。
Promise Func
function Promise(fn) {
if (!(this instanceof Promise))
throw new TypeError('Promises must be constructed via new');
if (typeof fn !== 'function') throw new TypeError('not a function');
this._state = 0;
this._handled = false;
this._value = undefined;
this._deferreds = [];
doResolve(fn, this);
}
- 可以看到这里大致限制了必须要使用new操作符调用Promise,还有我们的参数必须是函数类型的;
- 初始化属性(_state,__handled,_value,_deferreds),这些属性后面要用到;
- 先看一下_state
/** 内部状态码
* 0: pending,当前Promise正在执行中,默认值
* 1: fulfilled, 执行了resolve函数,且参数_value不是期约,即_value instanceof Promise === false
* 2: rejected,执行了reject函数
* 3: fulfilled,执行了resolve函数,且参数_value是期约,即_value instanceof Promise === true
*/
- _value为存放我们最终resolve或reject的结果
- _deferreds缓存then方法执行后生成的handle实例对象,把传入的回调缓存成handle实例对象的属性
- 调用doResolve(fn, this);
可以看到我们把fn和this传递给了doResolve执行,接着看看doResolve;
doResolve Func
function doResolve(fn, self) {
var done = false;
try {
fn(
function(value) {
if (done) return;
done = true;
resolve(self, value);
},
function(reason) {
if (done) return;
done = true;
reject(self, reason);
}
);
} catch (ex) {
if (done) return;
done = true;
reject(self, ex);
}
}
可以看到doResolve很简单,它直接调用了我们new Promise(fn)传递进去的fn,把两个匿名函数传递给了resolve,reject给我们在外部操作完成后调用,然后如果有出错会直接帮我们调用reject;
// 可以看回我们new Promise(fn)传递进去的fn
(resolve,reject) => {
setTimeout( () => resolve('result'),1000)
}
执行fn,我们的setTimeout在1000ms后调用了resolve('result');
传递给fn的resolve是
function(value) {
if (done) return;
done = true;
resolve(self, value);
}
这里的done实际上是为了防止我们多次调用resolve,只有第一次生效吧;
由于我们是异步调用resolve的,执行完setTimeout之后就会执行then,1000ms后才会调用resolve [我们往then Func里面看](#then Func) 由于我们fn里只调用了resolve,接下来我们直接看resolve
resolve Func
function resolve(self, newValue) {
try {
if (newValue === self)
throw new TypeError('A promise cannot be resolved with itself.');
if (
newValue &&
(typeof newValue === 'object' || typeof newValue === 'function')
) {
var then = newValue.then;
if (newValue instanceof Promise) {
self._state = 3;
self._value = newValue;
finale(self);
return;
} else if (typeof then === 'function') {
doResolve(bind(then, newValue), self);
return;
}
}
self._state = 1;
self._value = newValue;
finale(self);
} catch (e) {
reject(self, e);
}
}
- 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.
resolve主要是校验我们newValue的类型,我们的newValue是'result',自然是string
- self是通过Promise Func -> doResolve Func,传递下去的this,也就是首先判断的是newValue不能是new Promise的this本身
- newValue是object || function类型
- newValue是Promise实例,像我们上面说的_state会是3(),_把newValue赋值给我们最终resolve的结果,并执行finale(self);
- newValue.then是函数,会重新执行doResolve,不会执行finale;
- newValue不是Promise实例,newValue.then也不是函数,_state会是1(),把newValue赋值给我们最终resolve的结果,并执行finale(self);
很显然,我们的例子里resolve的类型效验会执行以上list->3,我们看看finale
finale Func
function finale(self) {
if (self._state === 2 && self._deferreds.length === 0) {
Promise._immediateFn(function() {
if (!self._handled) {
Promise._unhandledRejectionFn(self._value);
}
});
}
for (var i = 0, len = self._deferreds.length; i < len; i++) {
handle(self, self._deferreds[i]);
}
self._deferreds = null;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
_state为1,finale代码会执行
for (var i = 0, len = self._deferreds.length; i < len; i++) {
handle(self, self._deferreds[i]);
}
self._deferreds = null;
- 1.
- 2.
- 3.
- 4.