概述
从上次更新Promise/A+规范后,已经很久没有更新博客了。之前由于业务需要,完成了一个TypeScript语言的Promise库。这次我们来和大家一步一步介绍下,我们如何实现一个符合Promise/A+规范的Promise库。
如果对Promise/A+规范还不太了解的同学,建议先看看上一篇博客—— [译]前端基础知识储备——Promise/A+规范 。
实现流程
首先,我们来看下,在我实现的这一个Promise中,代码由下面这几部分组成:
-
全局异步函数执行器
-
常量与属性
-
类方法
-
类静态方法
通过上面这四个部分,我们就能够得到一个完整的Promise。这四个部分互相有关联,接下来我们一个一个模块来看。
全局异步函数执行器
在之前的Promiz的源码分析的博客中我有提到过,我们如何来实现一个异步函数执行器。通过JavaScript的执行原理我们可以知道,如果要实现异步执行相关函数的话,我们可以选择使用宏任务和微任务,这一点在Promise/A+的规范中也有提及。因此,下面我们提供了一个用宏任务来实现异步函数执行器的代码供大家参考。
let index = 0;
if (global.postMessage) {
global.addEventListener('message', (e) => {
if (e.source === global) {
let id = e.data;
if (isRunningTask) {
nextTick(functionStorage[id]);
} else {
isRunningTask = true;
try {
functionStorage[id]();
} catch (e) {
}
isRunningTask = false;
}
delete functionStorage[id];
functionStorage[id] = void 0;
}
});
}
function nextTick(func) {
if (global.setImmediate) {
global.setImmediate(func);
} else if (global.postMessage) {
functionStorage[++index] = func;
global.postMessage(index, '*')
} else {
setTimeout(func);
}
}
通过上面的代码我们可以看到,我们一共使用了setImmediate
、postMessage
、setTimeout
这三个添加宏任务的方法来进行一步函数执行。
常量与属性
说完了如何进行异步函数的执行,我们来看下相关的常量与属性。在实现Promise之前,我们需要定义一些常量和类属性,用于后面存储数据。让我们一个一个来看下。
常量
首先,Promise共有5个状态,我们需要用常量来进行定义,具体如下:
enum State {
pending = 0,
resolving = 1,
rejecting = 2,
resolved = 3,
rejected = 4
};
这五个常量分别对应Promise中的5个状态,相信大家能够从名字中理解,我们就不多讲了。
属性
在Promise中,我们需要一些属性来存储数据状态和后续的Promise引用,具体如下:
class Promise {
private _value;
private _reason;
private _next = [];
public state: State = 0;
public fn;
public er;
}
我们对属性进行逐一说明:
-
_value
,表示在resolved状态时,用来存储当前的值。 -
_reason
,表示在rejected状态时,用来存储当前的原因。 -
_next
,表示当前Promise后面跟着then
函数的引用。 -
fn
,表示当前Promise中的then
方法的第一个回调函数。 -
er
,表示当前Promise中的then
方法的的第二个回调函数(即catch
的第一个参数,下面看catch
实现方法就能理解)。
类方法
看完了常量与类的属性,我们来看下类的静态方法。
Constructor
首先,如果我们要实现一个Promise,我们需要一个构造函数来初始化最初的Promise。具体代码如下:
class Promise {
constructor(resolver?) {
if (typeof resolver !== 'function' && resolver !== undefined) {
throw TypeError()
}
if (typeof this !== 'object') {
throw TypeError()
}
try {
if (typeof resolver === 'function') {
resolver(this.resolve.bind(this), this.reject.bind(this));
}
} catch (e) {
this.reject(e);
}
}
}
从Promise/A+的规范来看,我们可以知道,如果resolver
存在并且不是一个function的话,那么我们就应该抛出一个错误;否则,我们应该将resolve
和reject
方法传给resolver
作为参数。
resolve && reject
那么,resolve
和reject
方法又是做什么的呢?这两个方法主要是用来让当前的这个Promise转换状态的,即从pend