实现 Promise/A+ 规范
阅文前置
首先了解清楚 Promise/A+ 规范组成部分
Promise
的三种状态:pending、fulfilled 和 rejected
- 状态只能从
pending
转到fulfilled
或者rejected
,并且状态一旦改变就不会再变 - Promise 实现需要遵守
thenable
接口规范,then
方法用来注册回调函数,then
必须返回一个新的 Promise 实例
- then 方法支持
链式调用
,当前 Promise 达成或拒绝后,会调用后面 then 中的回调函数 - catch 方法用于指定 Promise reject 时的回调函数
- finally 方法用于指定无论结果如何都会执行的回调函数。
- Promise 需要有一个
resolve 和 reject
静态方法用来改变 Promise 的状态 - Promise 静态
all、race、allSettled、any
方法
如何实现以上所述
- 定义一个类名称为
GPromise
将实现Promise/A+
规范 - 实现
GPromise 构造函数
- 实现
resolve 和 reject
方法 - 实现
then 、catch 、finally
方法 - 实现
all、race、allSettled、any
方法
实现步骤
1. 实现 GPromise 构造函数和基本状态
Promise 一开始状态都是 pending,只有通过调用 resolve 或 reject 才能将状态改变为 fulfilled 或 rejected
且状态不可逆- resolve 和 reject 用于传递异步操作的结果在异步操作成功时,调用 resolve 并传递结果值;在异步操作失败时,调用 reject 并传递失败原因
resolve 和 reject 可以触发 then/catch 后续操作一旦 Promise 状态改变,会触发然后链上的回调函数执行
- resolve 和 reject 隔离异步操作结果通过它们,可以在异步操作完成后再进行结果处理,异步操作结果的值和状态的改变都被包装在 Promise 内部
- 保证 executor 函数的标准格式 Promise 接受一个 executor 执行器函数,规范中 executor 需要传入 resolve 和 reject 两个函数参数以处理异步结果
代码
js
/**
* Promise 状态枚举
*/
const PENDING = 'pending';
const FULFILLED = 'fulFilled';
const REJECTED = 'rejected';
class GPromise {
constructor(executor) {
// 初始化状态为 pending
this.status = PENDING;
// 初始化成功的值为 undefined
this.value = undefined;
// 初始化失败的值为 undefined
this.reason = undefined;
// 初始化成功处理函数队列
this.onFulfilledCBS = [];
// 初始化失败处理函数队列
this.onRejectedCBS = [];
// 定义resolve方法
const resolve = (value) => {
// 只有在pending状态才能更改状态和值
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 执行所有成功处理函数
this.onFulfilledCBS.forEach((callback) => callback());
}
};
// 定义reject方法
const reject = (reason) => {
// 只有在pending状态才能更改状态和原因
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 执行所有失败处理函数
this.onRejectedCBS.forEach((callback) => callback());
}
};
// 立即执行 executor,处理异常
try {
executor(resolve, reject);
} catch (error) {
// 如果执行器函数抛出异常,将Promise状态更改为rejected
reject(error);
}
}
}
解释 executor 立即执行函数
executor
函数的作用是启动Promise
并传递异步操作的结果- 当异步操作成功时,调用 resolve,将结果值传递出去,Promise 的状态会变为 fulfilled
- 当异步操作失败时,调用 reject,将错误原因传递出去,Promise 的状态会变为 rejected
executor 会在 Promise 实例创建时立即执行,异步操作的启动会放在 executor 中
- 然后 Promise 通过then/catch等方法指定回调,在异步操作完成后,根据结果调用对应回调函数
- 所以 executor 是 Promise 的一个关键,它内部运行的异步任务会影响 Promise 的状态变化
- 通过它,Promise 可以知道异步操作什么时候结束,并采取相应的后续操作
2. 实现 then 方法
- 注册回调then 接收两个参数,onFulfilled 和 onRejected,分别代表 Promise 成功或失败的回调。
js
promise.then(onFulfilled, onRejected);
- 返回新的 Promise then 方法会返回一个新的 Promise 实例,这样就可以实现 Promise 的链式调用。
js
promise.then().then().then();
- 回调执行 Promise 如果成功了,会执行 onFulfilled,把值传给它;如果失败了,会执行 onRejected,把原因传给它
- 传递值 then 方法会将上一个 Promise 的结果作为参数,传递给下一个 then 的回调函数
- 处理错误在 then 的回调函数中,如果抛出异常,那么 then 返回的 Promise 状态为 rejected
- 异步执行 then 方法总是会返回一个新的 Promise,即使回调是同步的,then 的调用也会延迟到当前调用栈清空后再执行
先封装一个工具方法 resolvePromise
用来处理 then
方法链式调用处理 resolve 或者 reject
js
/**
* 解析 Promise 链式调用中的结果值x,并决定是执行resolve还是reject
*
* @param {Promise} promise2 - 下一个新的Promise对象
* @param {any} x - 当前Promise回调return的值
* @param {Function} resolve - 执行下一个Promise的resolve方法
* @param {Function} reject - 执行下一个Promise的reject方法
*/
function resolvePromise(promise2, x, resolve, reject) {
// 如果值 x 和 promise2 是同一个引用,以 TypeError 为据因拒绝 promise2
// 这是为了防止死循环
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 标记是否已调用,防止多次调用
let called = false;
// 2. 如果 x 是 GPromise 实例
if (x instanceof GPromise) {
// 根据 x 的状态调用 resolve 或 reject
x.then(
(y) => {
resolvePromise(promise2, y, resolve, reject);
},
(reason) => {
reject(reason);
}
);
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 3. 如果 x 是对象或函数
try {
// 获取 x 的 then 方法
const then = x.then;
if (typeof then === 'function') {
// 如果 then 是函数
// 使用 x 作为上下文调用 then 方法
then.call(
x,
(y) => {
// 成功回调
if (called) return; // 如果已经调用过,直接返回
called = true;
// 递归处理 y
resolvePromise(promise2, y, resolve, reject);
},
(reason) => {
// 失败回调
if (called) return; // 如果已经调用过,直接返回
called = true;
reject(reason);
}
);
} else {
// 如果 then 不是函数
// 直接调用 resolve
resolve(x);
}
} catch (error) {
// 如果获取或调用 then 方法抛出异常
if (called) return; // 如果已经调用过,直接返回
called = true;
reject(error);
}
} else {
// 4. 如果 x 不是对象或函数直接调用 resolve
resolve(x);
}
}
对 then 方法实现代码
js
then(onFulfilled, onRejected) {
// 处理默认回调函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === 'function'
? onRejected
: (reason) => {
throw reason;
};
// 创建新的Promise
const promise2 = new GPromise((resolve, reject) => {
// 根据状态区分处理
if (this.status === FULFILLED) {
// 使用setTimeout保证异步调用
setTimeout(() => {
try {
// 调用onFulfilled,并获取返回值
const x = onFulfilled(this.value);
// 使用返回值x和新的Promise实例promise2来处理resolve和reject
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
// 如果处理函数抛出异常,则将promise2状态更改为rejected
reject(error);
}
}, 0);
} else if (this.status === REJECTED) {
// 使用setTimeout保证异步调用
setTimeout(() => {
try {
// 调用onRejected,并获取返回值
const x = onRejected(this.reason);
// 使用返回值x和新的Promise实例promise2来处理resolve和reject
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
// 如果处理函数抛出异常,则将promise2状态更改为rejected
reject(error);
}
}, 0);
} else if (this.status === PENDING) {
// 如果当前Promise状态仍为pending,将处理函数加入相应的队列中
this.onFulfilledCBS.push(() => {
// 使用setTimeout保证异步调用
setTimeout(() => {
try {
// 调用onFulfilled,并获取返回值
const x = onFulfilled(this.value);
// 使用返回值x和新的Promise实例promise2来处理resolve和reject
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
// 如果处理函数抛出异常,则将promise2状态更改为rejected
reject(error);
}
}, 0);
});
this.onRejectedCBS.push(() => {
// 使用setTimeout保证异步调用
setTimeout(() => {
try {
// 调用onRejected,并获取返回值
const x = onRejected(this.reason);
// 使用返回值x和新的Promise实例promise2来处理resolve和reject
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
// 如果处理函数抛出异常,则将promise2状态更改为rejected
reject(error);
}
}, 0);
});
}
});
// 返回 promise2 以支持链式调用
return promise2;
}
实现 catch 方法
js
catch(onRejected) {
// 调用then方法,仅传入失败处理函数
return this.then(null, onRejected);
}
实现 finally 方法
js
/**
* 主要思路就是:
* 1. 保存 this 引用到 self
* 2. 根据当前状态立即执行或者推入回调队列
* 3. 在回调中先执行 callback,再执行 resolve/reject
* 4. 最后返回 self 而不是新的 Promise
* 5. 这样可以避免创建新的 Promise,直接在原 Promise 对象上操作。
*/
finally(callback) {
let self = this;
return new GPromise((resolve, reject) => {
const onFinally = () => {
queueMicrotask(() => {
try {
callback();
} catch (error) {
reject(error);
}
});
};
const onFulfilled = (value) => {
onFinally();
resolve(value);
};
const onRejected = (reason) => {
onFinally();
reject(reason);
};
switch (self.status) {
case FULFILLED:
onFulfilled(self.value);
break;
case REJECTED:
onRejected(self.reason);
break;
case PENDING:
self.onFulfilledCBS.push(onFulfilled);
self.onRejectedCBS.push(onRejected);
break;
}
});
}
静态方法 resolve
js
static resolve(value) {
if (value instanceof GPromise) {
return value;
}
return new GPromise((resolve, reject) => {
resolve(value);
});
}
- 如果传入的值已经是一个 Promise 实例,那么直接返回这个实例。
- 否则,返回一个新的 Promise 实例,并在 executor 中立即调用 resolve 将传入的值作为成功结果。
- 之所以需要作这个判断和处理,是因为 Promise.resolve 可以接受不同类型的参数:
- 如果是一个 Promise 实例,直接返回即可,不需要做处理
- 如果是一个 thenable 对象,需要转换为一个 Promise 对象
- 如果是原始类型的值,需要用一个 resolved Promise 包装起来
- 这样,Promise.resolve 可以把不同的值统一转换为 Promise 对象,使其更易于处理
静态方法 reject
js
static reject(reason) {
return new GPromise((resolve, reject) => {
reject(reason);
});
}
静态方法 all
将多个 Promise
包装成一个新的 Promise
,只有当所有的 Promise 都 resolve 时
,新的 Promise 才会 resolve,一旦有任意一个 Promise reject
,新的 Promise 就会 reject
js
static all(promises) {
return new GPromise((resolve, reject) => {
const result = [];
let resolvedCount = 0;
promises.forEach((promise, index) => {
GPromise.resolve(promise).then(
(value) => {
result[index] = value;
resolvedCount++;
if (resolvedCount === promises.length) {
resolve(result);
}
},
(reason) => {
reject(reason);
}
);
});
});
}
这里的关键逻辑:
- 使用递归 Promise.resolve 处理非 Promise 值
- 保存每个 promise 结果到结果数组
- 计数追踪成功的 promise 数量
- 当全部成功则 resolve,任何一个失败则 reject
静态方法 race
将多个 Promise 包装成一个新的 Promise,新的 Promise 一旦有一个 Promise resolve或者reject的话,新的Promise就会采取对应状态
js
static race(promises) {
return new GPromise((resolve, reject) => {
promises.forEach((promise) => {
GPromise.resolve(promise).then(
(value) => {
resolve(value);
},
(reason) => {
reject(reason);
}
);
});
});
}
静态方法 allSettled
将多个 Promise 包装成一个新的 Promise,只有等到所有这些参数实例都返回结果
,不管是 resolved 还是 rejected,新的 Promise 才会结束
js
static allSettled(promises) {
return new GPromise((resolve, reject) => {
const result = [];
let settledCount = 0;
promises.forEach((promise, index) => {
GPromise.resolve(promise).then(
(value) => {
result[index] = { status: FULFILLED, value };
settledCount++;
if (settledCount === promises.length) {
resolve(result);
}
},
(reason) => {
result[index] = { status: REJECTED, reason };
settledCount++;
if (settledCount === promises.length) {
resolve(result);
}
}
);
});
});
}
静态方法 any
将多个 Promise 包装成一个新的 Promise,只要其中的一个 Promise resolve,新的 Promise 就会 resolve,全部失败
则会返回错误原因
js
static any(promises) {
return new GPromise((resolve, reject) => {
const errors = [];
let rejectedCount = 0;
promises.forEach((promise, index) => {
GPromise.resolve(promise).then(
(value) => {
resolve(value);
},
(reason) => {
errors[index] = reason;
rejectedCount++;
if (rejectedCount === promises.length) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
}
);
});
});
}
使用 Promise/A+ 测试包跑一下
-
安装 Promise/A+ 测试库
npm install promises-aplus-tests -D
-
在项目中创建测试文件 test.js,引入测试库
js
const test = require('promises-aplus-tests');
const adapter = require('./utils/adapter');
test(adapter, function (err) {
if (err) {
console.error('Promises/A+ 测试失败:');
console.error(err);
} else {
console.log('Promises/A+ 测试通过');
}
});
-
创建测试适配器适配器是一个类,需要实现以下方法:
- deferred() - 返回 {promise, resolve, reject} 对象
- resolved(value) - 返回resolved的Promise
- rejected(reason) - 返回rejected的Promise
-
适配器 adapter.js :
js
const { GPromise } = require('../js/GPromise'); // 导入我们实现的 GPromise 模块
// 暴露适配器对象
module.exports = {
resolved: GPromise.resolve,
rejected: GPromise.reject,
deferred() {
const result = {};
result.promise = new GPromise((resolve, reject) => {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
};
-
终端执行脚本
node test.js
测试 GPromise 结果
完整代码结构
js
/**
* Promise状态枚举
*/
const PENDING = 'pending';
const FULFILLED = 'fulFilled';
const REJECTED = 'rejected';
class GPromise {
constructor(executor) {
// 初始化状态为pending
this.status = PENDING;
// 初始化成功的值为undefined
this.value = undefined;
// 初始化失败的原因为undefined
this.reason = undefined;
// 初始化成功处理函数队列
this.onFulfilledCBS = [];
// 初始化失败处理函数队列
this.onRejectedCBS = [];
// 定义resolve方法
const resolve = (value) => {
// 只有在pending状态才能更改状态和值
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 执行所有成功处理函数
this.onFulfilledCBS.forEach((callback) => callback());
}
};
// 定义reject方法
const reject = (reason) => {
// 只有在pending状态才能更改状态和原因
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 执行所有失败处理函数
this.onRejectedCBS.forEach((callback) => callback());
}
};
// 立即执行 executor,处理异常
try {
executor(resolve, reject);
} catch (error) {
// 如果执行器函数抛出异常,将Promise状态更改为rejected
reject(error);
}
}
/**
* then 方法,参数分别为成功和失败的回调
*
* @param {Function} onFulfilled - 成功回调
* @param {Function} onRejected - 失败回调
* @return {Promise} 返回一个新的 Promise 对象
*/
then(onFulfilled, onRejected) {
// 处理默认回调函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === 'function'
? onRejected
: (reason) => {
throw reason;
};
// 创建新的Promise
const promise2 = new GPromise((resolve, reject) => {
// 根据状态区分处理
if (this.status === FULFILLED) {
// 使用setTimeout保证异步调用
setTimeout(() => {
try {
// 调用onFulfilled,并获取返回值
const x = onFulfilled(this.value);
// 使用返回值x和新的Promise实例promise2来处理resolve和reject
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
// 如果处理函数抛出异常,则将promise2状态更改为rejected
reject(error);
}
}, 0);
} else if (this.status === REJECTED) {
// 使用setTimeout保证异步调用
setTimeout(() => {
try {
// 调用onRejected,并获取返回值
const x = onRejected(this.reason);
// 使用返回值x和新的Promise实例promise2来处理resolve和reject
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
// 如果处理函数抛出异常,则将promise2状态更改为rejected
reject(error);
}
}, 0);
} else if (this.status === PENDING) {
// 如果当前Promise状态仍为pending,将处理函数加入相应的队列中
this.onFulfilledCBS.push(() => {
// 使用setTimeout保证异步调用
setTimeout(() => {
try {
// 调用onFulfilled,并获取返回值
const x = onFulfilled(this.value);
// 使用返回值x和新的Promise实例promise2来处理resolve和reject
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
// 如果处理函数抛出异常,则将promise2状态更改为rejected
reject(error);
}
}, 0);
});
this.onRejectedCBS.push(() => {
// 使用setTimeout保证异步调用
setTimeout(() => {
try {
// 调用onRejected,并获取返回值
const x = onRejected(this.reason);
// 使用返回值x和新的Promise实例promise2来处理resolve和reject
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
// 如果处理函数抛出异常,则将promise2状态更改为rejected
reject(error);
}
}, 0);
});
}
});
// 返回 promise2 以支持链式调用
return promise2;
}
catch(onRejected) {
// 调用then方法,仅传入失败处理函数
return this.then(null, onRejected);
}
/**
* 主要思路就是:
* 1. 保存 this 引用到 self
* 2. 根据当前状态立即执行或者推入回调队列
* 3. 在回调中先执行 callback,再执行 resolve/reject
* 4. 最后返回 self 而不是新的 Promise
* 5. 这样可以避免创建新的 Promise,直接在原 Promise 对象上操作。
*/
finally(callback) {
let self = this;
return new GPromise((resolve, reject) => {
const onFinally = () => {
queueMicrotask(() => {
try {
callback();
} catch (error) {
reject(error);
}
});
};
const onFulfilled = (value) => {
onFinally();
resolve(value);
};
const onRejected = (reason) => {
onFinally();
reject(reason);
};
switch (self.status) {
case FULFILLED:
onFulfilled(self.value);
break;
case REJECTED:
onRejected(self.reason);
break;
case PENDING:
self.onFulfilledCBS.push(onFulfilled);
self.onRejectedCBS.push(onRejected);
break;
}
});
}
static resolve(value) {
if (value instanceof GPromise) {
return value;
}
return new GPromise((resolve, reject) => {
resolve(value);
});
}
static reject(reason) {
return new GPromise((resolve, reject) => {
reject(reason);
});
}
static all(promises) {
return new GPromise((resolve, reject) => {
const result = [];
let resolvedCount = 0;
// 遍历 promises 获取每个 promise 的结果
promises.forEach((promise, index) => {
// 处理非 Promise 值
GPromise.resolve(promise).then(
// promise 成功的回调
(value) => {
// 在对应下标位置保存结果
result[index] = value;
resolvedCount++;
// 如果全部成功
if (resolvedCount === promises.length) {
// resolve 新 promise
resolve(result);
}
},
(reason) => {
reject(reason);
}
);
});
});
}
static race(promises) {
return new GPromise((resolve, reject) => {
promises.forEach((promise) => {
GPromise.resolve(promise).then(
(value) => {
resolve(value);
},
(reason) => {
reject(reason);
}
);
});
});
}
static allSettled(promises) {
return new GPromise((resolve, reject) => {
const result = [];
let settledCount = 0;
promises.forEach((promise, index) => {
GPromise.resolve(promise).then(
(value) => {
result[index] = { status: FULFILLED, value };
settledCount++;
if (settledCount === promises.length) {
resolve(result);
}
},
(reason) => {
result[index] = { status: REJECTED, reason };
settledCount++;
if (settledCount === promises.length) {
resolve(result);
}
}
);
});
});
}
static any(promises) {
return new GPromise((resolve, reject) => {
const errors = [];
let rejectedCount = 0;
promises.forEach((promise, index) => {
GPromise.resolve(promise).then(
(value) => {
resolve(value);
},
(reason) => {
errors[index] = reason;
rejectedCount++;
if (rejectedCount === promises.length) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
}
);
});
});
}
}
/**
* 解析 Promise 链式调用中的结果值x,并决定是执行resolve还是reject
*
* @param {Promise} promise2 - 下一个新的Promise对象
* @param {any} x - 当前Promise回调return的值
* @param {Function} resolve - 执行下一个Promise的resolve方法
* @param {Function} reject - 执行下一个Promise的reject方法
*/
function resolvePromise(promise2, x, resolve, reject) {
// 如果值 x 和 promise2 是同一个引用,以 TypeError 为据因拒绝 promise2
// 这是为了防止死循环
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 标记是否已调用,防止多次调用
let called = false;
// 2. 如果 x 是 GPromise 实例
if (x instanceof GPromise) {
// 根据 x 的状态调用 resolve 或 reject
x.then(
(y) => {
resolvePromise(promise2, y, resolve, reject);
},
(reason) => {
reject(reason);
}
);
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 3. 如果 x 是对象或函数
try {
// 获取 x 的 then 方法
const then = x.then;
if (typeof then === 'function') {
// 如果 then 是函数
// 使用 x 作为上下文调用 then 方法
then.call(
x,
(y) => {
// 成功回调
if (called) return; // 如果已经调用过,直接返回
called = true;
// 递归处理 y
resolvePromise(promise2, y, resolve, reject);
},
(reason) => {
// 失败回调
if (called) return; // 如果已经调用过,直接返回
called = true;
reject(reason);
}
);
} else {
// 如果 then 不是函数
// 直接调用 resolve
resolve(x);
}
} catch (error) {
// 如果获取或调用 then 方法抛出异常
if (called) return; // 如果已经调用过,直接返回
called = true;
reject(error);
}
} else {
// 4. 如果 x 不是对象或函数直接调用 resolve
resolve(x);
}
}
module.exports = {
GPromise
};
·······························································································································