前言
大家平时基本上都听过或者用过Promise,大部分基本都是拿来主义(当然也包括我),而对于原理知之甚少,这样不利于日后问题的排查和代码的优化。为此,我写这篇文章来加深对Promise的理解和使用。本文假设读者都是对Promise的使用有一定了解的基础上展开的,因此没对Promise的基本使用做过多的介绍。然后,我将通过以下几个部分来讲解:
- 基本实现
- 扩展功能实现
- 扩展API实现
基本实现
State Machine(状态机)
Promise本质上其实是一个状态机,由PENDING
、FULFILLED
、REJECTED
这个三个状态组成,初始状态为PENDING。因此,我们创建一个简单的状态机,state为当前的状态,value为结果值,deferrals为延迟处理的任务(当Promise状态处于PENDING
时添加的任务),deferralsState为延迟任务的数量(0表示没有延迟任务,1表示一个延迟任务,2表示两个及以上延迟任务)。
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
function Promise() {
this.state = PENDING;
this.value = null;
this.deferralsState = 0;
this.deferrals = null;
}
Transitions(转换器)
根据规定,Promise状态的改变是有限制的,即只能PENDING
->FULFILLED
或PENDING
->REJECTED
,而且这种改变是不可逆的。除此之外,当处于FULFILLED
状态下,Promise必须要一个value,而且这个value是不可改变的;当处于REJECTED
状态下,Promise必须要有一个reason(报错),而且这个reason也是不可改变的。
我们创建resolve
和reject
来表示Promise状态的改变并把调用者传递过来的值赋给value。可是调用者可能传各种类型的值过来,如果传Promise进来,我们是无法直接取值的,那么我们就通过then函数(后面会介绍)来取值,并通过递归调用doResolve函数(后面会介绍)来把参数Promise中的value传递到我们当前Promise中,而递归调用的目的是为了防止多重嵌套情况的发生。那finale
的作用是什么呢?之前我们说过,当Promise还处于PENDING
状态的时候,我们会把添加的待办任务放入deferrals中,在状态改为FULFILLED
或 REJECTED
后,我们统一处理。
function resolve(self, newValue) {
if (
newValue &&
(typeof newValue === "object" || typeof newValue === "function")
) {
var then = getThen(newValue);
if (then === IS_ERROR) {
return reject(self, LAST_ERROR);
}
if (typeof then === "function") {
doResolve(then.bind(newValue), self);
return;
}
}
self.state = FULFILLED;
self.value = newValue;
finale(self);
}
function reject(self, newValue) {
self.state = REJECTED;
self.value = newValue;
finale(self);