妙思维-WEB前端高级工程师养成计划「全修精英特训」(无秘分享

23 阅读4分钟

Promises/A+ 规范与源码重写及周边

Promises/A+ 是一种用于处理异步操作的标准规范,旨在解决回调地狱问题并提供一致的接口。理解 Promises/A+ 规范及其实现有助于更好地掌握 JavaScript 中的异步编程模式。本文将详细介绍 Promises/A+ 规范,并通过源码重写一个符合该规范的 Promise 实现。

妙思维-WEB前端高级工程师养成计划【全修精英特训】完整无密_789it

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 处理异步回调

为了处理异步回调,我们需要维护两个数组来存储 fulfilledrejected 的回调函数。

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.resolvePromise.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.allPromise.race。希望这些内容能帮助你深入理解 JavaScript 中的异步编程,并在实际项目中灵活运用这些知识。