关于前端:现代JavaScript高级教程实现符合PromiseA规范的Promise

2次阅读

共计 9060 个字符,预计需要花费 23 分钟才能阅读完成。

点击在线浏览,体验更好 链接
古代 JavaScript 高级小册 链接
深入浅出 Dart 链接
古代 TypeScript 高级小册 链接

实现合乎 Promise/A+ 标准的 Promise

介绍:

Promise 是 JavaScript 中解决异步操作的重要工具之一。Promise/A+ 标准是一种对于 Promise 实现的规范,它定义了 Promise 的行为和办法。本文将具体介绍如何实现 Promise/A+ 标准,让你理解 Promise 的工作原理并可能本人实现一个符合规范的 Promise。

Promise/A+ 标准简介

1. Promise 的三种状态:

  • pending(进行中):Promise 的初始状态,示意异步操作正在执行。
  • fulfilled(已实现):异步操作胜利实现,并返回一个值,称为解决值(fulfillment value)。
  • rejected(已回绝):异步操作失败或被回绝,并返回一个起因(reason),通常是一个谬误对象。

2. 状态转换:

  • Promise 的状态只能从 pending 转变为 fulfilled 或 rejected,一旦转变就不可逆转。
  • 状态转换是由异步操作的后果决定的。如果异步操作胜利实现,Promise 的状态会转变为 fulfilled;如果异步操作失败或被回绝,Promise 的状态会转变为 rejected。

3. Promise 的根本办法:

  • then办法:用于注册异步操作胜利实现时的回调函数,并返回一个新的 Promise 对象。它承受两个参数:onFulfilled(可选,异步操作胜利时的回调函数)和 onRejected(可选,异步操作失败时的回调函数)。
  • catch办法:用于注册异步操作失败时的回调函数,并返回一个新的 Promise 对象。它是 then 办法的一个非凡模式,仅用于捕捉异样。
  • finally办法:无论异步操作胜利或失败,都会执行的回调函数,并返回一个新的 Promise 对象。它在 Promise 链中的最初执行,并且不接管任何参数。

4. 谬误冒泡和异样传递:

  • Promise/A+ 标准要求 Promise 的谬误可能被适当地捕捉和解决。当一个 Promise 产生谬误时,它会向下流传,直到找到最近的谬误处理函数为止。
  • 在 Promise 链中的任何一个 Promise 产生谬误,都会导致整个链上的谬误处理函数被调用,以便进行错误处理和复原。

遵循 Promise/A+ 标准的实现应该具备上述个性,以确保统一的 Promise 行为和接口。这样,开发者能够编写通用的异步代码,而无需放心特定 Promise 实现的差异性。

实现 Promise

当从零开始实现 Promise/A+ 标准的 Promise,咱们须要逐渐构建 Promise 的外围性能,包含状态治理、状态转换、回调解决和错误处理。

步骤 1: 创立 Promise 构造函数

首先,咱们须要创立一个 Promise 构造函数,它承受一个执行器函数作为参数。执行器函数承受两个参数,即 resolve 和 reject 函数,用于管制 Promise 的状态转换。

function MyPromise(executor) {// TODO: 实现构造函数}

步骤 2: 初始化 Promise 状态和回调

在构造函数中,咱们须要初始化 Promise 的状态和回调数组。状态能够应用一个变量来示意,初始值为 ‘pending’。回调数组用于存储注册的胜利和失败回调函数。

function MyPromise(executor) {
  var self = this;
  self.state = 'pending';
  self.value = undefined;
  self.reason = undefined;
  self.onFulfilledCallbacks = [];
  self.onRejectedCallbacks = [];

  // TODO: 实现构造函数的其余部分
}

步骤 3: 实现 resolve 和 reject 函数

咱们须要实现 resolve 和 reject 函数,用于将 Promise 的状态从 ‘pending’ 转换为 ‘fulfilled’ 或 ‘rejected’。resolve 函数将传递一个值来兑现 Promise,而 reject 函数将传递一个起因来回绝 Promise。

function MyPromise(executor) {
  var self = this;
  self.state = 'pending';
  self.value = undefined;
  self.reason = undefined;
  self.onFulfilledCallbacks = [];
  self.onRejectedCallbacks = [];

  function resolve(value) {if (self.state === 'pending') {
      self.state = 'fulfilled';
      self.value = value;
      self.onFulfilledCallbacks.forEach(function(callback) {callback(self.value);
      });
    }
  }

  function reject(reason) {if (self.state === 'pending') {
      self.state = 'rejected';
      self.reason = reason;
      self.onRejectedCallbacks.forEach(function(callback) {callback(self.reason);
      });
    }
  }

  try {executor(resolve, reject);
  } catch (e) {reject(e);
  }
}

步骤 4: 实现 then 办法

接下来,咱们须要实现 then 办法,用于注册胜利和失败的回调函数,并返回一个新的 Promise。then 办法承受两个参数:胜利回调函数和失败回调函数。

function MyPromise(executor) {
  var self = this;
  self.state = 'pending';
  self.value = undefined;
  self.reason = undefined;
  self.onFulfilledCallbacks = [];
  self.onRejectedCallbacks = [];

  function resolve(value) {if (self.state === 'pending') {
      self.state = 'fulfilled';
      self.value = value;
      self.onFulfilledCallbacks.forEach(function(callback) {callback(self.value);
      });
    }
  }

  function reject(reason) {if (self.state === 'pending') {
      self.state = 'rejected';
      self.reason = reason;
      self.onRejectedCallbacks.forEach(function(callback) {callback(self.reason);
      });
    }
  }

  try {
    executor

(resolve, reject);
  } catch (e) {reject(e);
  }
}

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  var self = this;
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value) {return value;};
  onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {throw reason;};

  var newPromise = new MyPromise(function(resolve, reject) {// TODO: 实现 then 办法的其余部分});

  return newPromise;
};

步骤 5: 解决 Promise 状态转换和回调执行

咱们须要在 then 办法中解决 Promise 的状态转换和回调的执行。依据以后 Promise 的状态,咱们能够立刻执行回调函数或将回调函数增加到相应的回调数组中。

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  var self = this;
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value) {return value;};
  onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {throw reason;};

  var newPromise = new MyPromise(function(resolve, reject) {function handleFulfilled(value) {
      try {var x = onFulfilled(value);
        resolvePromise(newPromise, x, resolve, reject);
      } catch (e) {reject(e);
      }
    }

    function handleRejected(reason) {
      try {var x = onRejected(reason);
        resolvePromise(newPromise, x, resolve, reject);
      } catch (e) {reject(e);
      }
    }

    if (self.state === 'fulfilled') {setTimeout(function() {handleFulfilled(self.value);
      }, 0);
    } else if (self.state === 'rejected') {setTimeout(function() {handleRejected(self.reason);
      }, 0);
    } else if (self.state === 'pending') {self.onFulfilledCallbacks.push(function(value) {setTimeout(function() {handleFulfilled(value);
        }, 0);
      });

      self.onRejectedCallbacks.push(function(reason) {setTimeout(function() {handleRejected(reason);
        }, 0);
      });
    }
  });

  return newPromise;
};

步骤 6: 解析 Promise

最初,咱们须要实现 resolvePromise 函数,用于解析 Promise。它会解决 thenable 和非 thenable 值,并依据其状态执行相应的解决。

function resolvePromise(promise, x, resolve, reject) {if (promise === x) {reject(new TypeError('Circular reference detected.'));
  }

  if (x && typeof x === 'object' || typeof x === 'function') {
    var called = false;

    try {
      var then = x.then;

      if (typeof then === 'function') {
        then.call(
          x,
          function(y) {if (!called) {
              called = true;
              resolvePromise(promise, y, resolve, reject);
            }
          },
          function(r) {if (!called) {
              called = true;
              reject(r);
            }
          }
        );
      } else {resolve(x);
      }
    } catch (e) {if (!called) {
        called = true;
        reject(e);
      }
    }
  } else {resolve(x);
  }
}

Promise 的测试与调试

1. 装置 Jest:

确保在我的项目中装置了 Jest。能够应用 npm 或 yarn 进行装置。

npm install jest --save-dev

2. 编写单元测试:

在我的项目中创立一个测试文件,以 .test.js 为后缀,编写单元测试用例来验证 Promise 的各个性能和办法的正确性。例如,能够编写测试用例来验证状态转换、回调函数的执行、链式调用等方面的行为是否合乎预期。

// promise.test.js

const {MyPromise} = require('./promise');

describe('MyPromise', () => {it('should fulfill with correct value', () => {const promise = new MyPromise((resolve, reject) => {setTimeout(() => {resolve('success');
      }, 100);
    });

    return promise.then((value) => {expect(value).toBe('success');
    });
  });

  it('should reject with correct reason', () => {const promise = new MyPromise((resolve, reject) => {setTimeout(() => {reject(new Error('failure'));
      }, 100);
    });

    return promise.catch((reason) => {expect(reason).toBeInstanceOf(Error);
      expect(reason.message).toBe('failure');
    });
  });

  // 更多测试用例...
});

3. 运行测试:

应用 Jest 运行编写的测试用例,执行 Promise 的测试。

npx jest

4. 模仿异步操作:

应用 setTimeout 函数或 Promise.resolve 等办法来模仿异步操作,并确保 Promise 在正确的机会进行状态转换和回调函数的执行。例如,能够应用 setTimeout 来模仿异步操作的实现。

const promise = new MyPromise((resolve, reject) => {setTimeout(() => {resolve('success');
  }, 100);
});

promise.then((value) => {console.log(value); // 输入: success
});

5. 调试 Promise 链:

在开发过程中,可能会遇到 Promise 链上的问题,如回调函数不执行、后果不合乎预期等。能够应用 console.logdebugger语句来打印两头变量的值,或者应用 Jest 的调试性能来逐渐跟踪 Promise 链的执行过程。

const promise1 = new MyPromise((resolve, reject) => {setTimeout(() => {resolve('success');
  }, 100);
});

const promise2 = promise1.then((value) => {console.log(value); // 输入: success
  return value.toUpperCase();});

promise2.then((value) => {console.log(value); // 输入: SUCCESS
});

能够应用 Jest 的 --inspect 参数进行调试,或者在代码中增加 debugger 语句来触发断点。

npx jest --inspect

应用 Promise/A+ 测试套件

应用 Promise/A+ 测试套件是确保 Promise 实现符合规范的重要步骤。Promise/A+ 测试套件是一组针对 Promise 实现的测试用例,可用于验证 Promise 是否合乎 Promise/A+ 标准的要求。

以下是应用 Promise/A+ 测试套件的步骤:

  1. 下载 Promise/A+ 测试套件:
    首先,从 Promise/A+ 官网的 GitHub 仓库(https://github.com/promises-aplus/promises-tests)下载 Promise/A+ 测试套件的代码。将其保留到我的项目的测试目录中。
  2. 集成测试套件:
    将 Promise/A+ 测试套件集成到我的项目的测试环境中,确保能够运行测试套件并取得后果。
  3. 实现 Promise 接口:
    依据 Promise/A+ 标准的要求,实现一个符合规范的 Promise 类。确保 Promise 类的行为和接口与标准统一。
  4. 运行测试套件:
    应用测试框架(如 Mocha、Jest 等)运行 Promise/A+ 测试套件。在测试套件的配置中,指定测试文件为 Promise/A+ 测试套件的入口文件。
  5. 验证后果:
    查看测试套件的运行后果。如果所有的测试用例都通过,示意 Promise 实现合乎 Promise/A+ 标准。如果有测试用例失败,依据测试后果来调试和修复 Promise 实现中的问题。

上面是一个示例,展现如何应用 Promise/A+ 测试套件进行测试:

// 装置 Promise/A+ 测试套件
npm install promises-aplus-tests --save-dev

// 集成 Promise/A+ 测试套件到测试环境中
const promisesAplusTests = require('promises-aplus-tests');
const {MyPromise} = require('./promise');

// 运行 Promise/A+ 测试套件
promisesAplusTests(MyPromise, function (err) {
  // 测试实现后的回调函数
  if (err) {console.error('Promise/A+ 测试失败:', err);
  } else {console.log('Promise/A+ 测试通过!');
  }
});

在上述代码中,MyPromise是本人实现的 Promise 类。通过将 MyPromise 传递给 promisesAplusTests 函数,将 Promise 类集成到 Promise/A+ 测试套件中。运行测试后,将会失去测试后果。

通过应用 Promise/A+ 测试套件,能够验证本人实现的 Promise 是否合乎 Promise/A+ 标准的要求,确保 Promise 的行为和接口的一致性。

Promise 其它 API

要实现 Promise.allPromise.race等其余 API,能够依据 Promise 的标准和性能需要来编写相应的代码。以下是对这两个 API 的实现进行开展解说的代码示例:

  1. 实现 Promise.all
    Promise.all 办法接管一个可迭代对象(如数组或类数组对象),并返回一个新的 Promise,该 Promise 在所有输出的 Promise 都胜利实现时才会胜利,否则将会失败。返回的 Promise 的解决值是一个由所有输出 Promise 解决值组成的数组。
Promise.all = function (promises) {return new Promise((resolve, reject) => {const results = [];
    let completedCount = 0;

    const checkCompletion = () => {if (completedCount === promises.length) {resolve(results);
      }
    };

    for (let i = 0; i < promises.length; i++) {promises[i]
        .then((value) => {results[i] = value;
          completedCount++;
          checkCompletion();})
        .catch((reason) => {reject(reason);
        });
    }

    if (promises.length === 0) {resolve(results);
    }
  });
};

应用示例:

const promise1 = new Promise((resolve) => setTimeout(() => resolve(1), 1000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 2000));
const promise3 = new Promise((resolve) => setTimeout(() => resolve(3), 1500));

Promise.all([promise1, promise2, promise3])
  .then((results) => {console.log(results); // 输入: [1, 2, 3]
  })
  .catch((reason) => {console.error(reason);
  });
  1. 实现 Promise.race
    Promise.race 办法接管一个可迭代对象(如数组或类数组对象),并返回一个新的 Promise,该 Promise 将与最先解决或回绝的输出 Promise 具备雷同的状态。
Promise.race = function (promises) {return new Promise((resolve, reject) => {for (let i = 0; i < promises.length; i++) {promises[i]
        .then((value) => {resolve(value);
        })
        .catch((reason) => {reject(reason);
        });
    }
  });
};

应用示例:

const promise1 = new Promise((resolve) => setTimeout(() => resolve(1), 1000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 2000));
const promise3 = new Promise((resolve) => setTimeout(() => resolve(3), 1500));

Promise.race([promise1, promise2, promise3])
  .then((value) => {console.log(value); // 输入: 1
  })
  .catch((reason) => {console.error(reason);
  });

参考资料

  • Promise/A+ 标准官网文档:https://promisesaplus.com/
正文完
 0