在前端开发中,Promise
是一个特别重要的概念,不少的异步操做都依赖于 Promise
。既然在平常中和它打过那么多的交道,那么咱们来本身实现一个 Promise
,加深对 Promise
的理解,加强本身的 JavaScript
功力。javascript
本次在实现 Promise
的同时会使用 jest
编写测试用例,以保证明现过程的正确性。前端
若是想看测试框架的搭建或者完整实现的,能够点击个人github 仓库进行查看,若是你喜欢,欢迎 star,若是你发现个人错误,欢迎提出来。java
这是一份开放、健全且通用的 Promise
实现规范。由开发者制定,供开发者参考。git
这里是官方规范,照着官方规范去实现,就能够写一个属于本身的、符合标准的 Promise
。github
话很少说,咱们来开始根据 A+ 规范实现 Promise
。数组
规范的第一节是对一些术语的表达,并没有实际功能无需实现。promise
Promise
是一个类(JavsScript
的类是用函数实现的,只是一个语法糖),它必须接受一个函数,不然报错;它还有一个 then
方法。框架
class PROMISE {
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`);
}
}
then() {}
}
复制代码
测试用例以下:异步
import Promise from '../';
describe('Promise', () => {
test('是一个类', () => {
expect(Promise).toBeInstanceOf(Function);
expect(Promise.prototype).toBeInstanceOf(Object);
});
test('new Promise() 必须接受一个函数', () => {
expect(() => {
// @ts-ignore
new Promise();
}).toThrow(TypeError);
expect(() => {
// @ts-ignore
new Promise('promise');
}).toThrow(TypeError);
expect(() => {
// @ts-ignore
new Promise(null);
}).toThrow(TypeError);
});
test('new Promise(fn) 会生成一个对象,对象有 then 方法', () => {
const promise = new Promise(() => {});
expect(promise.then).toBeInstanceOf(Function);
});
})
复制代码
测试用例我后面再也不列出来了,有兴趣的能够去个人 github 仓库查看。函数
Promise
有三种状态:请求态(pending
)、完成态(fulfilled
)和拒绝态(rejected
)。Promise
一开始是请求态,它能够转为另外两种状态(只容许改变一次),它会在状态改变的时候获得一个值。
class PROMISE {
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`);
}
// 初始状态
this.status = 'pending';
// 初始值
this.value = null;
// class 内部默认是严格模式,因此须要绑定 this
executor(this.resolve.bind(this), this.reject.bind(this));
}
then() {}
resolve(value) {
// 状态保护
if (this.status !== 'pending') {
return;
}
// 改变状态, 赋值
this.status = 'fulfilled';
this.value = value;
}
reject(reason) {
// 状态保护
if (this.status !== 'pending') {
return;
}
// 改变状态, 赋值
this.status = 'rejected';
this.value = reason;
}
}
复制代码
到这里,咱们就差很少实现了规范 2.1。
then
能够接受两个函数,会在状态改变以后异步执行,根据规范 2.2.1,若是它们不是函数,就忽略。then
的异步本是一个微任务,这里用宏任务 setTimeout
就将代替一下(若是你想了解微任务、宏任务的知识,欢迎点这里查看我写的关于 Event Loop
的文章)。
then(onFulfilled, onRejected) {
// 暂时将它们变成空函数,后面再作修改
if (typeof onFulfilled !== 'function') {
onFulfilled = () => {};
}
if (typeof onRejected !== 'function') {
onRejected = () => {};
}
if (this.status === 'fulfilled') {
// 异步执行,将就用 setTimeout 实现
setTimeout(() => {
onFulfilled(this.value);
});
}
if (this.status === 'rejected') {
setTimeout(() => {
onRejected(this.value);
});
}
}
复制代码
这里的状况是期待执行 then
函数时,Promise
的状态已经获得了改变。
若是 Promise
的执行函数是一个异步函数,执行 then
的时候,Promise
的状态还没获得改变,那么就须要把 then
接受的两个函数保存起来,等到 resolve
或 reject
的时候执行,这里也要异步执行。
then(onFulfilled, onRejected) {
// 暂时将它们变成同步的空函数,后面再作修改
if (typeof onFulfilled !== 'function') {
onFulfilled = () => {};
}
if (typeof onRejected !== 'function') {
onRejected = () => {};
}
// 若是执行 then 的时候,Promise 状态还未发生变化,就先将这两个函数存起来
if (this.status === 'pending') {
this.callbacks.push({
onFulfilled: () => {
setTimeout(() => {
onFulfilled();
});
},
onRejected: () => {
setTimeout(() => {
onRejected();
});
}
});
}
if (this.status === 'fulfilled') {
// 异步执行,将就用 setTimeout 实现
setTimeout(() => {
// 2.2.5
onFulfilled.call(undefined, this.value);
});
}
if (this.status === 'rejected') {
setTimeout(() => {
// 2.2.5
onRejected.call(undefined, this.value);
});
}
}
resolve(value) {
// 状态保护
if (this.status !== 'pending') {
return;
}
// 改变状态, 赋值
this.status = 'fulfilled';
this.value = value;
// 若是回调函数数组中有值,说明以前执行过 then,须要调用 then 接受的函数
this.callbacks.forEach((callback) => {
// 2.2.5
callback.onFulfilled.call(undefined, value);
});
}
reject(reason) {
// 状态保护
if (this.status !== 'pending') {
return;
}
// 改变状态, 赋值
this.status = 'rejected';
this.value = reason;
// 若是回调函数数组中有值,说明以前执行过 then,须要调用 then 接受的函数
this.callbacks.forEach((callback) => {
// 2.2.5
callback.onRejected.call(undefined, reason);
});
}
复制代码
这样一来,规范的 2.2.2 和 2.2.3 就都实现了。
并且因为 then
里面的 onFulfilled
和 onRejected
都是异步执行的,因此它也知足规范 2.2.4,它会在 Promise
的代码执行以后被调用。
根据规范 2.2.5, onFulfilled
和 onRejected
调用时也不存在 this
,因此用 .call
调用,指定 undefined
为 this
。
规范 2.2.6,若是 then
执行以前 Promise
已经改变了状态,那么直接执行多个 then
。不然将 then
的函数参数存在 callbacks
数组中,后面依次调用,实现规范 2.2.6。
Promise
有一个 then
方法,then
以后还能够 then
,那么让 then
返回一个 Promise
便可,根据规范 2.2.7,咱们也必须让 then
返回一个 Promise
。
根据规范 2.2.7.1,不管是 onFulfilled
和 onrejected
,它们返回的值都会被当作 then
返回的新的 Promise
的 resolve
的值成功处理。
根据规范 2.2.7.2,若是 onFulfilled
和 onRejected
抛出了一个错误 e
,那么会被当作 then
返回的新的 Promise
的 reject
的值失败处理。
then(onFulfilled, onRejected) {
// then 返回一个 Promise
return new PROMISE((resolve, reject) => {
// 暂时将它们变成空函数,后面再作修改
if (typeof onFulfilled !== 'function') {
onFulfilled = () => {};
}
if (typeof onRejected !== 'function') {
onRejected = () => {};
}
// 若是执行 then 的时候,Promise 状态还未发生变化,就先将这两个函数存起来
if (this.status === 'pending') {
this.callbacks.push({
// 这里也须要变了
onFulfilled: () => {
setTimeout(() => {
try {
// 2.2.5
const result = onFulfilled.call(undefined, this.value);
// onFulfilled 的返回值当作新的 Promise 的 resolve 的值被调用
resolve(result);
} catch (error) {
// 若是抛出了错误,那么当作新的 Promise 的 reject 的值被调用
reject(error);
}
});
},
onRejected: () => {
setTimeout(() => {
try {
// 2.2.5
const result = onRejected.call(undefined, this.value);
// onRejected 的返回值当作新的 Promise 的 resolve 的值被调用
resolve(result);
} catch (error) {
// 若是抛出了错误,那么当作新的 Promise 的 reject 的值被调用
reject(error);
}
});
}
});
}
if (this.status === 'fulfilled') {
// 异步执行,将就用 setTimeout 实现
setTimeout(() => {
try {
// 2.2.5
const result = onFulfilled.call(undefined, this.value);
// onFulfilled 的返回值当作新的 Promise 的 resolve 的值被调用
resolve(result);
} catch (error) {
// 若是抛出了错误,那么当作新的 Promise 的 reject 的值被调用
reject(error);
}
});
}
if (this.status === 'rejected') {
setTimeout(() => {
try {
// 2.2.5
const result = onRejected.call(undefined, this.value);
// onRejected 的返回值当作新的 Promise 的 resolve 的值被调用
resolve(result);
} catch (error) {
// 若是抛出了错误,那么当作新的 Promise 的 reject 的值被调用
reject(error);
}
});
}
});
}
复制代码
规范 2.2.7.3 和 2.2.7.4 表示若是 then
的 onFulfilled
和 onRejected
不是函数,那么新的 Promise
会用上一个 Promise
成功(resolve
)或失败(reject
)的值继续成功或失败,也就是会继承上一个 Promise
的状态和值。
举一个例子
new Promise((resolve, reject) => {
/** * 执行函数体 */
})
.then()
.then(
function A() {},
function B() {}
);
复制代码
由于第一个 then
的参数不是函数,那么会发生穿透传递,因此后一个 then
接受的两个参数 function A 和 function B,会根据最前面那个 Promise
的状态和值来进行调用。
也就是上面的代码其实和下面的代码执行结果同样。
new Promise((resolve, reject) => {
/** * 执行函数体 */
})
.then(
function A() {},
function B() {}
);
复制代码
好的,让咱们来实现这个规范。
其实很简单,你上一个 Promise
若是是 resolve
时,那么我用 then
的 Promise
也 resolve
,且值不变,若是是 reject
,那么 then
的 Promise
也 reject
,且值不变。
这里稍微一点点绕,但愿你把这里仔细看,彻底搞明白。
if (typeof onFulfilled !== 'function') {
onFulfilled = (value) => {
// 前面的 Promise 是 resolve 时,会调用 onFulfilled
// 那么 then 的新 Promise 也 resolve
// 将状态和值传递给 then 的 then
resolve(value);
};
}
if (typeof onRejected !== 'function') {
onRejected = (reason) => {
// 前面的 Promise 是 reject 时,会调用 onRejected
// 那么 then 的新 Promise 也 reject
// 将状态和值传递给 then 的 then
reject(reason);
};
}
复制代码
其实这里能够简化一下,变成下面这种。
if (typeof onFulfilled !== 'function') {
onFulfilled = resolve;
}
if (typeof onRejected !== 'function') {
onRejected = reject;
}
复制代码
这样,Promise
的链式操做就完成了。
接下来咱们继续看规范 2.3 的部分。
若是 Promise
和 resolve
或者 reject
调用的值是同一个,那么应该使 Promise
处于拒绝(reject
)态,值为 TypeError
。
代码以下:
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`);
}
// 初始状态
this.status = 'pending';
// 初始值
this.value = null;
// 初始回调数组
this.callbacks = [];
// class 内部默认是严格模式,因此须要绑定 this
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
// 接住 resolve 和 reject 抛出的 TypeError,做为 reject 的值调用
this.reject(error);
}
}
/** * 代码 */
resolve(value) {
// 状态保护
if (this.status !== 'pending') {
return;
}
// 若是 promise 和 resolve 调用的值是同一个,那么就抛出错误
if (value === this) {
throw new TypeError();
}
// 改变状态, 赋值
this.status = 'fulfilled';
this.value = value;
// 若是回调函数数组中有值,说明以前执行过 then,须要调用 then 接受的函数
this.callbacks.forEach((callback) => {
callback.onFulfilled.call(undefined, value);
});
}
reject(reason) {
// 状态保护
if (this.status !== 'pending') {
return;
}
// 若是 promise 和 reject 调用的值是同一个,那么就抛出错误
if (value === this) {
throw new TypeError();
}
// 改变状态, 赋值
this.status = 'rejected';
this.value = reason;
this.callbacks.forEach((callback) => {
callback.onRejected.call(undefined, reason);
});
}
复制代码
规范 2.3.2 是 2.3.3 状况的一个子集,咱们直接实现 2.3.3 就能够了。
规范 2.3.3 说了那么多,其实就是在 resolve
和 reject
中添加下面几行代码。
if (value instanceof Object) {
// 2.3.3.1 2.3.3.2
const then = value.then;
// 2.3.3.3
if (typeof then === 'function') {
return then.call(
value,
this.resolve.bind(this),
this.reject.bind(this)
);
}
}
复制代码
关于规范 2.3.3.2,我理解的是,并非说 x.then 是一个异常,而是在取值的过程当中发生了一个异常,代码表达以下:
// 不是这种
const X = {
then: new Error()
}
// 是相似这种的状况
const x = {};
Object.defineProperty(x, 'then', {
get: function() {
throw new Error('y');
}
});
new Promise((resolve, reject) => {
resolve(x)
}).then((value) => {
console.log('fulfilled', value)
}, (reason) => {
console.log('rjected', reason)
})
复制代码
因为取值 x.then 的过程当中抛出了一个异常,被 constructor
中的 try catch
捕捉到了,执行 reject
,这里就无需作处理了。
规范 2.3.4 不用特殊实现,说的就是正常状况。
到这里就把 A+ 规范走了一遍,实现的 Promise
以下:
class PROMISE {
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`);
}
// 初始状态
this.status = 'pending';
// 初始值
this.value = null;
// 初始回调数组
this.callbacks = [];
// class 内部默认是严格模式,因此须要绑定 this
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
// 接住 resolve 和 reject 抛出的 TypeError,做为 reject 的值调用
this.reject(error);
}
}
then(onFulfilled, onRejected) {
// then 返回一个 Promise
return new PROMISE((resolve, reject) => {
// then 的穿透传递
if (typeof onFulfilled !== 'function') {
onFulfilled = resolve;
}
if (typeof onRejected !== 'function') {
onRejected = reject;
}
// 若是执行 then 的时候,Promise 状态还未发生变化,就先将这两个函数存起来
if (this.status === 'pending') {
this.callbacks.push({
onFulfilled: () => {
setTimeout(() => {
try {
const result = onFulfilled.call(undefined, this.value);
// onFulfilled 的返回值当作新的 Promise 的 resolve 的值被调用
resolve(result);
} catch (error) {
// 若是抛出了错误,那么当作新的 Promise 的 reject 的值被调用
reject(error);
}
});
},
onRejected: () => {
setTimeout(() => {
try {
const result = onRejected.call(undefined, this.value);
// onRejected 的返回值当作新的 Promise 的 resolve 的值被调用
resolve(result);
} catch (error) {
// 若是抛出了错误,那么当作新的 Promise 的 reject 的值被调用
reject(error);
}
});
}
});
}
if (this.status === 'fulfilled') {
// 异步执行,将就用 setTimeout 实现
setTimeout(() => {
try {
const result = onFulfilled.call(undefined, this.value);
// onFulfilled 的返回值当作新的 Promise 的 resolve 的值被调用
resolve(result);
} catch (error) {
// 若是抛出了错误,那么当作新的 Promise 的 reject 的值被调用
reject(error);
}
});
}
if (this.status === 'rejected') {
setTimeout(() => {
try {
const result = onRejected.call(undefined, this.value);
// onRejected 的返回值当作新的 Promise 的 resolve 的值被调用
resolve(result);
} catch (error) {
// 若是抛出了错误,那么当作新的 Promise 的 reject 的值被调用
reject(error);
}
});
}
});
}
resolve(value) {
// 状态保护
if (this.status !== 'pending') {
return;
}
// 若是 promise 和 resolve 调用的值是同一个,那么就抛出错误
if (value === this) {
throw new TypeError('Chaining cycle detected for promise');
}
if (value instanceof Object) {
// 2.3.3.1
const then = value.then;
// 2.3.3.3
if (typeof then === 'function') {
return then.call(
value,
this.resolve.bind(this),
this.reject.bind(this)
);
}
}
// 改变状态, 赋值
this.status = 'fulfilled';
this.value = value;
// 若是回调函数数组中有值,说明以前执行过 then,须要调用 then 接受的函数
this.callbacks.forEach((callback) => {
callback.onFulfilled.call(undefined, value);
});
}
reject(reason) {
// 状态保护
if (this.status !== 'pending') {
return;
}
// 若是 promise 和 reject 调用的值是同一个,那么就抛出错误
if (reason === this) {
throw new TypeError('Chaining cycle detected for promise');
}
if (reason instanceof Object) {
// 2.3.3.1
const then = reason.then;
// 2.3.3.3
if (typeof then === 'function') {
return then.call(
reason,
this.resolve.bind(this),
this.reject.bind(this)
);
}
}
// 改变状态, 赋值
this.status = 'rejected';
this.value = reason;
this.callbacks.forEach((callback) => {
callback.onRejected.call(undefined, reason);
});
}
}
复制代码
其中重复的代码我在这里就不抽离出来了,这样方便阅读。
像 resolve
、reject
、all
、race
这几个静态方法其实不属于 A+ 规范中,我这里也顺带实现一下。
resolve
和 reject
相似,接受一个值,返回一个 Promise
。若是接受的值是一个 Promise
,那么就继承该 Promise
的状态和值。
static resolve(value) {
return new PROMISE((resolve, reject) => {
if (value instanceof PROMISE) {
value.then(resolve, reject);
} else {
resolve(value);
}
});
}
static reject(reason) {
return new PROMISE((resolve, reject) => {
if (reason instanceof PROMISE) {
reason.then(resolve, reject);
} else {
reject(reason);
}
});
}
复制代码
all
是接受一个 Promise
数组,返回一个 Promise
。
这里定义一个 results
数组,而后遍历 Promise
数组,每 resolve
一个 Promise
,就像 results
加入一个 resolve
的值,若是results
的长度与 Promise
数组的长度相同,那么说明所有的 Promise
都 resolve
了,那么 all
返回的 Promise
就 resolve
这个数组。
另外,只要 Promise
数组中有一个 Promise
转为了 reject
,那么 all
返回的 Promise
也 reject
掉。
static all(promiseArray) {
return new PROMISE((resolve, reject) => {
const results = [];
promiseArray.forEach((promise) => {
promise.then((value) => {
results.push(value);
if (results.length === promiseArray.length) {
resolve(results);
}
}, reject);
});
});
}
复制代码
race
也是接受一个 Promise
数组,返回一个 Promise
。
只有 Promise
数组中有一个 Promise
resolve
或者 reject
了,那么 race
返回的 Promise
也 resolve
或者 reject
。
static race(promiseArray) {
return new PROMISE((resolve, reject) => {
promiseArray.forEach((promise) => {
promise.then(resolve, reject);
});
});
}
复制代码
从头实现一遍 Promise
的 A+ 规范的过程当中,对 Promise
的一些细枝细节都梳理了一遍,一些以前根本没有注意到的地方也给暴露出来了,特别是 x 若是是一个有 then
方法的对象,那么 x 会被包装成一个 Promise
,这个地方也是以前没有接触到的。
这个过程我用 TypeScript
实现了一遍,具体代码点这里,其中也包括了我写的测试用例,若是你喜欢,欢迎 star。
若是你发现我实现过程有不对的地方,欢迎与我探讨。