Promises/A+ 规范与源码重写及周边
Promises/A+ 是一种用于处理异步操作的标准规范,旨在解决回调地狱问题并提供一致的接口。理解 Promises/A+ 规范及其实现有助于更好地掌握 JavaScript 中的异步编程模式。本文将详细介绍 Promises/A+ 规范,并通过源码重写一个符合该规范的 Promise 实现。
1. Promises/A+ 规范概述
Promises/A+ 规范定义了 Promise
对象的行为和方法,主要包括以下几个方面:
- 状态(State) :一个 Promise 只能处于三种状态之一:
-
pending
(待定)fulfilled
(已成功)rejected
(已失败)
- 值(Value) :当 Promise 被
fulfilled
时,它会有一个值;当被rejected
时,它会有一个原因(reason)。 - 链式调用(Chaining) :可以通过
.then()
方法注册回调函数,并返回一个新的 Promise 对象,从而支持链式调用。 - 异常处理(Error Handling) :可以通过
.catch()
方法捕获错误。
2. 核心概念
2.1 Promise 状态转换
pending
->fulfilled
:表示异步操作成功完成。pending
->rejected
:表示异步操作失败。- 一旦状态从
pending
转换为fulfilled
或rejected
,状态就不能再改变。
2.2 then 方法
then(onFulfilled, onRejected)
:注册两个回调函数,分别在 Promise 成功或失败时调用。
javascript深色版本promise.then(onFulfilled, onRejected);
2.3 异常传播
- 如果
onFulfilled
或onRejected
抛出异常,则后续的 Promise 会被rejected
。 - 如果
onFulfilled
返回一个 Promise,则后续的 Promise 会等待这个 Promise 完成。
3. 实现一个符合 Promises/A+ 规范的 Promise
我们将从头开始实现一个简单的、符合 Promises/A+ 规范的 Promise。
3.1 基本结构
javascript深色版本function MyPromise(executor) { let self = this; self.status = 'pending'; // 初始状态 self.value = undefined; // 成功时的值 self.reason = undefined; // 失败时的原因 function resolve(value) { if (self.status === 'pending') { self.status = 'fulfilled'; self.value = value; } } function reject(reason) { if (self.status === 'pending') { self.status = 'rejected'; self.reason = reason; } } try { executor(resolve, reject); } catch (err) { reject(err); }}
3.2 添加 then
方法
javascript深色版本MyPromise.prototype.then = function(onFulfilled, onRejected) { let self = this; // 处理未传入回调的情况 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }; switch (self.status) { case 'fulfilled': setTimeout(() => { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (err) { reject(err); } }, 0); break; case 'rejected': setTimeout(() => { try { let x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch (err) { reject(err); } }, 0); break; case 'pending': // 如果状态是 pending,则需要保存回调函数 let promise2 = new MyPromise((resolve, reject) => { self.onFulfilledCallbacks.push(() => { setTimeout(() => { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (err) { reject(err); } }, 0); }); self.onRejectedCallbacks.push(() => { setTimeout(() => { try { let x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch (err) { reject(err); } }, 0); }); }); break; }};
3.3 处理异步回调
为了处理异步回调,我们需要维护两个数组来存储 fulfilled
和 rejected
的回调函数。
javascript深色版本function MyPromise(executor) { let self = this; self.status = 'pending'; self.value = undefined; self.reason = undefined; self.onFulfilledCallbacks = []; // 存储 fulfilled 回调 self.onRejectedCallbacks = []; // 存储 rejected 回调 function resolve(value) { if (self.status === 'pending') { self.status = 'fulfilled'; self.value = value; self.onFulfilledCallbacks.forEach(fn => fn()); } } function reject(reason) { if (self.status === 'pending') { self.status = 'rejected'; self.reason = reason; self.onRejectedCallbacks.forEach(fn => fn()); } } try { executor(resolve, reject); } catch (err) { reject(err); }}
3.4 处理返回值
根据 Promises/A+ 规范,如果 then
方法返回的是一个 Promise,则需要等待这个 Promise 完成后再继续执行后续的操作。
javascript深色版本function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) { return reject(new TypeError('循环引用')); } let called; if (x != null && (typeof x === 'object' || typeof x === 'function')) { try { let then = x.then; if (typeof then === 'function') { then.call(x, y => { if (called) return; called = true; resolvePromise(promise2, y, resolve, reject); }, err => { if (called) return; called = true; reject(err); }); } else { resolve(x); } } catch (err) { if (called) return; called = true; reject(err); } } else { resolve(x); }}
4. 测试与验证
为了确保我们的实现符合 Promises/A+ 规范,可以使用 Promises/A+ Compliance Test Suite 进行测试。
安装测试工具:
bash深色版本npm install promises-aplus-tests --save-dev
编写测试脚本:
javascript深色版本const adapter = require('./my-promise');const test = require('promises-aplus-tests');test(adapter, function(err) { if (err) { console.error('测试失败:', err); } else { console.log('测试通过'); }});
adapter.js
文件内容如下:
javascript深色版本module.exports = MyPromise;
运行测试:
bash深色版本npx promises-aplus-tests adapter.js
5. 周边扩展
除了基本的 Promise 实现外,还可以扩展一些常用的功能:
5.1 Promise.all
Promise.all
接受一个 Promise 数组,并返回一个新的 Promise,当所有 Promise 都成功时才会成功,否则会立即失败。
javascript深色版本MyPromise.all = function(promises) { return new MyPromise((resolve, reject) => { let results = []; let count = 0; promises.forEach((promise, index) => { promise.then( value => { results[index] = value; count++; if (count === promises.length) { resolve(results); } }, reason => { reject(reason); } ); }); });};
5.2 Promise.race
Promise.race
接受一个 Promise 数组,并返回一个新的 Promise,只要其中一个 Promise 完成就会触发。
javascript深色版本MyPromise.race = function(promises) { return new MyPromise((resolve, reject) => { promises.forEach(promise => { promise.then( value => { resolve(value); }, reason => { reject(reason); } ); }); });};
5.3 Promise.resolve 和 Promise.reject
Promise.resolve
和 Promise.reject
分别用于创建一个已经成功或失败的 Promise。
javascript深色版本MyPromise.resolve = function(value) { return new MyPromise(resolve => resolve(value));};MyPromise.reject = function(reason) { return new MyPromise((_, reject) => reject(reason));};
结语
通过本文的介绍,我们了解了 Promises/A+ 规范的核心概念,并实现了一个符合该规范的简单 Promise 库。此外,我们还扩展了一些常用的 Promise 方法,如 Promise.all
和 Promise.race
。希望这些内容能帮助你深入理解 JavaScript 中的异步编程,并在实际项目中灵活运用这些知识。