Promises/A+规范

目录

 Promises/A+规范

术语:

要求: Promise状态:

Then 方法

参数可选

onFulfilled 特性

onRejected 特性

调用时机

调用要求

多次调用

返回

Promise解决过程 

注释:

  promise/a+规范测试

完整代码


 Promises/A+规范

一个开放的标准,用于实现者之间的可互操作的JavaScript Promise。

Promise代表异步操作的最终结果。与Promise交互的主要方式是通过其then方法,该方法注册回调函数以接收Promise的最终值或无法实现Promise的原因。

本规范详细说明了then方法的行为,提供了一个可互操作的基础,所有符合Promises/A+规范的Promise实现都可以依赖该基础。因此,该规范应被认为是非常稳定的。尽管Promises/A+组织可能会偶尔修订该规范以解决新发现的边缘情况,但我们只会在经过慎重考虑、讨论和测试后才会集成大的或不兼容的变更。

从历史上看,Promises/A+澄清了早期Promises/A提案的行为条款,扩展了其覆盖的事实行为,并省略了未明确规定或存在问题的部分。

最后,核心的Promises/A+规范不涉及如何创建、实现或拒绝Promise,而是选择专注于提供可互操作的then方法。在伴随规范的未来工作中可能会涉及这些主题

术语:

  • “promise”是一个具有符合本规范的then方法的对象或函数。
  • “thenable”是一个定义了then方法的对象或函数。
  • “value”是任何合法的JavaScript值(包括undefined、thenable或promise)。
  • “exception”是使用throw语句抛出的值。
  • “reason”是指示为什么Promise被拒绝的值。

要求: Promise状态:

  • Promise必须处于以下三种状态之一:pending(进行中)、fulfilled(已完成)或rejected(已拒绝)。
  • 当处于pending状态时,Promise可以转换为fulfilled或rejected状态。
  • 当处于fulfilled状态时,Promise不能转换为其他状态,必须具有一个值,并且该值不能改变。
  • 当处于rejected状态时,Promise不能转换为其他状态,必须具有一个原因,并且该原因不能改变。

Then 方法

这段话描述了Promise的then方法的规范。Promise是一种用于处理异步操作的对象,then方法用于注册回调函数,以便在Promise被履行(fulfilled)或被拒绝(rejected)时执行相应的操作。then方法接受两个参数,分别是在Promise被履行时调用的回调函数onFulfilled和在Promise被拒绝时调用的回调函数onRejected。这两个回调函数都是可选的,如果不提供,则会被忽略。

根据规范,当Promise被履行时,会按照注册的顺序执行所有的onFulfilled回调函数;当Promise被拒绝时,会按照注册的顺序执行所有的onRejected回调函数。同时,规范还定义了一些其他的行为,比如then方法可以多次调用同一个Promise,如果回调函数返回一个值,则会运行Promise解析过程来处理返回值,如果回调函数抛出异常,则Promise会被拒绝。

一个 promise 必须提供一个 then 方法以访问其当前值、终值和据因。

promise 的 then 方法接受两个参数:

promise.then(onFulfilled, onRejected)

参数可选

onFulfilled 和 onRejected 都是可选参数。

  • 如果 onFulfilled 不是函数,其必须被忽略
  • 如果 onRejected 不是函数,其必须被忽略

onFulfilled 特性

如果 onFulfilled 是函数:

  •  如果onFulfilled是一个函数: 在Promise被履行后,必须以Promise的值作为第一个参数调用它。
  •  在Promise被履行之前,不能调用它。 
  • 其调用次数不可超过一次

onRejected 特性

如果 onRejected 是函数:

  • 在Promise被拒绝后,必须以Promise的原因作为第一个参数调用它。
  •  在Promise被拒绝之前,不能调用它。
  • 其调用次数不可超过一次

调用时机

onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用 

调用要求

onFulfilled 和 onRejected 必须被作为函数调用(即没有 this 值)

多次调用

then 方法可以被同一个 promise 调用多次

  • 如果/当Promise被履行,所有相应的onFulfilled回调必须按照它们的原始调用顺序执行。
  • 如果/当Promise被拒绝,所有相应的onRejected回调必须按照它们的原始调用顺序执行。 

返回

then 方法必须返回一个 promise 对象

promise2 = promise1.then(onFulfilled, onRejected);   
  • 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程[[Resolve]](promise2, x)

简单来说,当一个Promise对象的成功回调或失败回调返回一个值时,这个值将被传递给Promise解决过程的[[Resolve]]步骤。这个步骤会根据这个值的类型和状态,决定如何处理这个Promise对象,可能会将其标记为已解决(fulfilled)或已拒绝(rejected),并执行相应的回调函数。

这个过程的目的是确保Promise对象能够正确地处理成功和失败的情况,并按照规范的要求进行相应的处理。

  • 如果onFulfilled或onRejected抛出异常e,则promise2必须以e作为原因被拒绝。
  • 如果onFulfilled不是一个函数且promise1被履行,promise2必须以与promise1相同的值被履行。
  • 如果onRejected不是一个函数且promise1被拒绝,promise2必须以与promise1相同的原因被拒绝。

Promise解决过程 

Promise解决过程是一个抽象操作,接受一个Promise和一个值作为输入,我们将其表示为[[Resolve]](promise, x)。如果x是一个thenable对象,它会尝试使promise采用x的状态,假设x至少有一些类似Promise的行为。否则,它会用值x来履行promise。

这种对thenable对象的处理允许Promise实现进行互操作,只要它们公开符合Promises/A+规范的then方法。它还允许Promises/A+实现通过合理的then方法来“吸收”不符合规范的实现。

要运行[[Resolve]](promise, x),执行以下步骤:

  1. 如果promise和x引用同一个对象,则以TypeError为原因拒绝promise。
  2. 如果x是一个Promise,采用其状态[3.4]:
    • 如果x是pending状态,则promise必须保持pending状态,直到x被履行或拒绝。
    • 当x被履行时,用相同的值履行promise。
    • 当x被拒绝时,用相同的原因拒绝promise。
  3. 否则,如果x是一个对象或函数:
    • 让then为x.then[3.5]。
    • 如果获取属性x.then导致抛出异常e,则以e为原因拒绝promise。
    • 如果then是一个函数,则以x作为this,第一个参数resolvePromise,第二个参数rejectPromise调用它,其中:
      • 如果resolvePromise被调用并传入一个值y,则运行[[Resolve]](promise, y)。
      • 如果rejectPromise被调用并传入一个原因r,则以r拒绝promise。
      • 如果resolvePromise和rejectPromise都被调用,或者对同一个参数进行多次调用,则以第一次调用为准,忽略任何进一步的调用。
      • 如果调用then抛出异常e:
        • 如果resolvePromise或rejectPromise已被调用,则忽略它。
        • 否则,以e为原因拒绝promise。
    • 如果then不是一个函数,则用x履行promise。
  4. 如果x既不是对象也不是函数,则用x履行promise。
  5. 如果一个Promise被解决为一个参与循环thenable链的thenable对象,以至于递归调用[[Resolve]](promise, thenable)最终再次调用[[Resolve]](promise, thenable),按照上述算法将导致无限递归。鼓励实现检测到这种递归并以一个有信息的TypeError为原因拒绝promise。

注释:

  • 这里的“平台代码”指的是引擎、环境和Promise实现代码。实际上,这个要求确保onFulfilled和onRejected在then被调用的事件循环轮次之后异步执行,并且在一个新的调用栈中执行。这可以通过“宏任务”机制(如setTimeout或setImmediate)或“微任务”机制(如MutationObserver或process.nextTick)来实现。由于Promise实现被认为是平台代码,它本身可能包含一个任务调度队列或“跳板”,在其中调用处理程序。
  • 在严格模式下,它们内部的this将为undefined;在非严格模式下,它将是全局对象。
  • 实现可以允许promise2 === promise1,前提是实现满足所有要求。每个实现应该记录它是否能产生promise2 === promise1,并在什么条件下。
  • 通常,只有当x来自当前实现时,才能知道x是一个真正的Promise。这个条款允许使用特定于实现的方法来采用已知符合规范的Promise的状态。
  • 首先存储对x.then的引用,然后测试该引用,然后调用该引用的过程,避免了对x.then属性的多次访问。这样的预防措施对于确保在访问器属性中的值可能在检索之间更改时的一致性非常重要。
  • 实现不应对thenable链的深度设置任意限制,并假设超过该任意限制递归将是无限的。只有真正的循环应该导致TypeError;如果遇到无限的不同thenable对象的链,无限递归是正确的行为。

  promise/a+规范测试

这段话是关于一个测试套件的说明。这个套件用于测试一个Promise实现是否符合Promises/A+规范。

通过在这个仓库中通过测试,意味着你的实现具有符合Promises/A+规范的then()方法,并且你可以在你的README中展示Promises/A+的标志。你还可以发送一个pull request,将你的实现列在实现页面上。

如何运行 这些测试可以在Node.js环境或者正确设置的浏览器环境中运行。

适配器 为了测试你的Promise库,你需要暴露一个非常简单的适配器接口。这些适配器以Node.js模块的形式编写,具有一些众所周知的导出项:

  • resolved(value):创建一个已解决的Promise,其值为value。 
  • rejected(reason):创建一个已拒绝的Promise,其拒因为reason。
  •  deferred():创建一个由{ promise, resolve, reject }组成的对象:
  •  promise是当前处于pending状态的Promise。
  •  resolve(value):将Promise解决为value。
  •  reject(reason):将Promise从pending状态转换为rejected状态,并提供拒因reason。

 resolved和rejected导出项实际上是可选的,如果它们不存在,测试运行器将使用deferred自动创建它们。但是,如果你的Promise库具有创建已解决或已拒绝Promise的能力,那么你应该包含这些导出项,以便测试运行器可以提供更好的代码覆盖率,并发现这些方法中的任何错误。

请注意,测试将永远不会将Promise或thenable作为解决值传递。这意味着我们从不直接使用接受Promise或thenable的resolve操作形式,而只使用直接的fulfillment操作,因为在没有给定thenable时,fulfill和resolve是等效的。

最后,请注意,包括deferred().resolve和deferred().reject在内的所有这些函数都不应该抛出异常。测试的结构无法处理异常,如果你的实现有可能抛出异常,比如在尝试解决一个已解决的Promise时抛出异常,你应该在编写适配器时使用try/catch来包装对你的实现的直接调用。

通过命令行界面 这个包带有一个命令行界面,可以通过全局安装

npm install promises-aplus-tests -g

或者将其包含在你的package.json的devDependencies中,并使用npm的scripts功能。在后一种情况下,你的设置可能如下所示:

{
    "devDependencies": {
        "promises-aplus-tests": "*"
    },
    "scripts": {
        "test": "run-my-own-tests && promises-aplus-tests test/my-adapter"
    }
}

命令行界面的第一个参数是适配器文件的文件名,相对于当前工作目录。它会尝试将后续的选项传递给Mocha,所以你可以使用例如--reporter spec或--grep 2.2.4。

以编程方式 这个包的主要导出是一个函数,允许你对适配器运行测试:

var promisesAplusTests = require("promises-aplus-tests");

promisesAplusTests(adapter, function (err) {
    // All done; output is in the console. Or check `err` for number of failures.
});

 你也可以将任何Mocha选项作为第二个参数传递,例如:

promisesAplusTests(adapter, { reporter: "dot" }, function (err) {
  // As before.
});

; 在现有的Mocha测试套件中 

如果你已经有一个Mocha测试套件,并希望将这些测试包含在其中,你可以这样做:

describe("Promises/A+ Tests", function () {
    require("promises-aplus-tests").mocha(adapter);
});

 如果你的Mocha测试在浏览器中运行,这也适用,只要你使用browserify即可。

完整代码

 * 测试手写promise是否符合promise/a+规范

 * 核心步骤

 * 1.使用CommonJS模块化语法暴露出去

 *  1.1提供deferred方法 返回对象[promise,reslove,reject]

 *  1.2 promise padding状态的promise实例

 *  1.3 reslove 以传入的原因兑现promise

 *  1.4 reject  以传入的原因拒绝promise

 * 2. 下包

 *  2.1初始化项目 npm init -y

 *  2.2npm install promises-aplus-tests -D

 * 3.配置script promises-aplus-tests 代码文件

 *

module.exports = {
  deferred() {
    const res = {};
    res.promise = new myPromise((resolve, reject) => {
      res.resolve = resolve;
      res.reject = reject;
    });
    return res;
  },
};

 代码

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class myPromise {
  // 状态
  state = PENDING;
  // 原因
  result = undefined;
  // 回调函数数组
  #handlers = []; // [{onFulfilled,onRejected}...]

  // 构造函数
  constructor(func) {
    // pending->fulfilled
    const resolve = (result) => {
      if (this.state === PENDING) {
        this.state = FULFILLED;
        this.result = result;
        this.#handlers.forEach(({ onFulfilled }) => {
          onFulfilled(this.result);
        });
      }
    };

    // pending->rejected
    const reject = (result) => {
      if (this.state === PENDING) {
        this.state = REJECTED;
        this.result = result;
        this.#handlers.forEach(({ onRejected }) => {
          onRejected(this.result);
        });
      }
    };

    try {
      func(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  // then方法
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (x) => x;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (x) => {
            throw x;
          };

    const p2 = new myPromise((resolve, reject) => {
      if (this.state === FULFILLED) {
        runAsynctask(() => {
          try {
            const x = onFulfilled(this.result);
            resolvePromise(p2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.state === REJECTED) {
        runAsynctask(() => {
          try {
            const x = onRejected(this.result);
            resolvePromise(p2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.state === PENDING) {
        this.#handlers.push({
          onFulfilled: () => {
            runAsynctask(() => {
              try {
                const x = onFulfilled(this.result);
                resolvePromise(p2, x, resolve, reject);
              } catch (error) {
                reject(error);
              }
            });
          },
          onRejected: () => {
            runAsynctask(() => {
              try {
                const x = onRejected(this.result);
                resolvePromise(p2, x, resolve, reject);
              } catch (error) {
                reject(error);
              }
            });
          },
        });
      }
    });

    return p2;
  }

  // catch方法
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

  // finally方法
  finally(onFinally) {
    return this.then(onFinally, onFinally);
  }

  // 静态方法-resolve
  static resolve(value) {
    if (value instanceof myPromise) {
      return value;
    }
    return new myPromise((resolve) => {
      resolve(value);
    });
  }

  // 静态方法-reject
  static reject(value) {
    return new myPromise((undefined, reject) => {
      reject(value);
    });
  }

  // 静态方法-race
  static race(promises) {
    return new myPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }
      promises.forEach((p) => {
        myPromise.resolve(p).then(
          (res) => {
            resolve(res);
          },
          (err) => {
            reject(err);
          }
        );
      });
    });
  }

  // 静态方法-all
  static all(promises) {
    return new myPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }
      promises.length === 0 && resolve(promises);
      const results = [];
      let count = 0;
      promises.forEach((p, index) => {
        myPromise.resolve(p).then(
          (res) => {
            results[index] = res;
            count++;
            count === promises.length && resolve(results);
          },
          (err) => {
            reject(err);
          }
        );
      });
    });
  }

  // 静态方法-allSettled
  static allSettled(promises) {
    return new myPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }
      promises.length === 0 && resolve(promises);

      const results = [];
      let count = 0;
      promises.forEach((p, index) => {
        myPromise.resolve(p).then(
          (res) => {
            results[index] = { status: FULFILLED, value: res };
            count++;
            count === promises.length && resolve(results);
          },
          (err) => {
            results[index] = { status: REJECTED, reason: err };
            count++;
            count === promises.length && resolve(results);
          }
        );
      });
    });
  }

  // 静态方法-any
  static any(promises) {
    return new myPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }
      promises.length === 0 &&
        reject(new AggregateError(promises, "All promises were rejected"));

      const errors = [];
      let count = 0;
      promises.forEach((p, index) => {
        myPromise.resolve(p).then(
          (res) => {
            resolve(res);
          },
          (err) => {
            errors[index] = err;
            count++;
            count === promises.length &&
              reject(new AggregateError(errors, "All promises were rejected"));
          }
        );
      });
    });
  }
}

function runAsynctask(callback) {
  if (typeof queueMicrotask === "function") {
    queueMicrotask(callback);
  } else if (typeof MutationObserver === "function") {
    const obs = new MutationObserver(callback);
    const divNode = document.createElement("div");
    obs.observe(divNode, { childList: true });
    divNode.innerText = "Javascript";
  } else {
    setTimeout(callback, 0);
  }
}
function resolvePromise(p2, x, resolve, reject) {
  //  如果p2和x引用同一个对象,通过TypeError作为原因来拒绝pormise
  if (x === p2) {
    throw new TypeError("Chaining cycle detected for promise");
  }

  /**
   * 如果x是一个promise,采用他的状态
   *    如果x是pengding状态,promise必须保持等待状态,直到x被fulfilled或rejected
   *    如果x是fulfilled状态,用相同的原因解决promise
   *     如果x是rejected状态,用相同的原因拒绝promise
   * */
  if (x instanceof myPromise) {
    x.then((y) => {
      resolvePromise(p2, y, resolve, reject);
    }, reject);
  }
  //  如果x是一个对象或者函数
  else if (x !== null && (typeof x === "object" || typeof x === "function")) {
    //  让then成为x.then
    try {
      var then = x.then;
    } catch (e) {
      //  如果检索属性x.then抛出了异常e,用e作为原因拒绝promise
      return reject(e);
    }

    /**
     * 如果then是一个函数,通过call调用他,并且将x作为他的this(参数1)
     * 调用then时传入2个回调函数:
     *    第一个参数叫做resolvePromise(对应到的参数2)
     *    第二个参数叫做rejectPromise(对应到参数3)
     * */

    if (typeof then === "function") {
      // 如果 resolvePromise 和 rejectPromise 均被调用,或者同一参数被调用了多次,只采用第一次调用,后续的调用会被忽略(观察called后续的赋值+判断)
      let called = false;
      try {
        then.call(
          x,
          // 如果 resolvePromise 以 成功原因 y 为参数被调用,继续执行 resolvePromise
          (y) => {
            if (called) return;
            called = true;
            resolvePromise(p2, y, resolve, reject);
          },
          // 如果 rejectPromise 以拒绝原因 r 为参数被调用,用 r 拒绝 promise
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } catch (e) {
        // 如果调用then抛出异常
        // 如果resolvePromise或rejectPromise已经被调用,忽略它
        if (called) return;
        called = true;

        //  否则以 e 作为拒绝原因 拒绝promise
        reject(e);
      }
    } else {
      // 如果then不是函数,用 x 作为原因 兑现promise
      resolve(x);
    }
  } else {
    // 如果then不是对象或函数,用 x 作为原因 兑现promise
    return resolve(x);
  }
}

module.exports = {
  deferred() {
    const res = {};
    res.promise = new myPromise((resolve, reject) => {
      res.resolve = resolve;
      res.reject = reject;
    });
    return res;
  },
};

你可能感兴趣的:(前端,javascript,开发语言)