gpt4 book ai didi

javascript - 了解Promises/A +规范

转载 作者:行者123 更新时间:2023-11-30 07:04:19 30 4
gpt4 key购买 nike

Promises/A+规范是最小的规范之一。因此,实现它是理解它的最佳方法。福布斯·林德赛(Forbes Lindesay)的以下回答将引导我们完成实现Promises / A +规范Basic Javascript promise implementation attempt的过程。但是,当我tested时,results并不令人满意:

✔ 109 tests passed
✘ 769 tests failed

显然,Promises / A +规范并不像看起来那样容易实现。您将如何实现规范并向新手解释代码?福布斯·林德赛(Forbes Lindesay)出色地解释了他的代码,但不幸的是他的实现是不正确的。

最佳答案

什么是 promise ?
Promise是一个thenable,其行为符合Promises/A+规范。thenable是具有then方法的任何对象或函数。
这是一个 promise 的样子:

var promise = {
...
then: function (onFulfilled, onRejected) { ... },
...
};
这是我们从一开始就了解的唯一 promise (不包括其行为)。
了解Promises / A +规范
Promises / A +规范分为三个主要部分:
  • promise 状态
  • then方法
  • promise 解决程序

  • 该规范没有提及如何创建,履行或拒绝 promise 。
    因此,我们将从创建这些函数开始:
    function deferred() { ... } // returns an object { promise, resolve, reject }

    function fulfill(promise, value) { ... } // fulfills promise with value
    function reject(promise, reason) { ... } // rejects promise with reason
    尽管没有创建 promise 的标准方法,但是 tests仍然要求我们公开 deferred函数。因此,我们将仅使用 deferred创建新的Promise:
  • deferred():创建一个由{ promise, resolve, reject }组成的对象:
  • promise是当前处于待处理状态的Promise。
  • resolve(value)使用value解决了 promise 。
  • reject(reason)将 promise 从挂起状态转移到拒绝状态,拒绝原因为reason


  • 这是 deferred函数的部分实现:
    function deferred() {
    var call = true;

    var promise = {
    then: undefined,
    ...
    };

    return {
    promise: promise,
    resolve: function (value) {
    if (call) {
    call = false;
    resolve(promise, value);
    }
    },
    reject: function (reason) {
    if (call) {
    call = false;
    reject(promise, reason);
    }
    }
    };
    }
    N.B.
  • promise对象仅具有then属性,当前为undefined。我们仍然需要决定then函数应该是什么以及promise对象应该具有哪些其他属性(即promise对象的形状)。该决定还将影响fulfillreject函数的实现。
  • resolve(promise, value)reject(promise, value)函数只能被调用一次,如果我们调用一个,那么我们就不能调用另一个。因此,我们将它们包装在一个闭包中,并确保它们之间仅被调用一次。
  • 我们在deferred的定义中引入了一个新函数,即 promise 解决程序resolve(promise, value)。规范将该函数表示为[[Resolve]](promise, x)。该功能的实现完全由规范规定。因此,我们接下来将实现它。

  • function resolve(promise, x) {
    // 2.3.1. If promise and x refer to the same object,
    // reject promise with a TypeError as the reason.
    if (x === promise) return reject(promise, new TypeError("Self resolve"));
    // 2.3.4. If x is not an object or function, fulfill promise with x.
    var type = typeof x;
    if (type !== "object" && type !== "function" || x === null)
    return fulfill(promise, x);
    // 2.3.3.1. Let then be x.then.
    // 2.3.3.2. If retrieving the property x.then results in a thrown exception e,
    // reject promise with e as the reason.
    try {
    var then = x.then;
    } catch (e) {
    return reject(promise, e);
    }
    // 2.3.3.4. If then is not a function, fulfill promise with x.
    if (typeof then !== "function") return fulfill(promise, x);
    // 2.3.3.3. If then is a function, call it with x as this, first argument
    // resolvePromise, and second argument rejectPromise, where:
    // 2.3.3.3.1. If/when resolvePromise is called with a value y,
    // run [[Resolve]](promise, y).
    // 2.3.3.3.2. If/when rejectPromise is called with a reason r,
    // reject promise with r.
    // 2.3.3.3.3. If both resolvePromise and rejectPromise are called,
    // or multiple calls to the same argument are made,
    // the first call takes precedence, and any further calls are ignored.
    // 2.3.3.3.4. If calling then throws an exception e,
    // 2.3.3.3.4.1. If resolvePromise or rejectPromise have been called, ignore it.
    // 2.3.3.3.4.2. Otherwise, reject promise with e as the reason.
    promise = deferred(promise);
    try {
    then.call(x, promise.resolve, promise.reject);
    } catch (e) {
    promise.reject(e);
    }
    }
    N.B.
  • 我们省略了section 2.3.2,因为它是一种优化的方法,它取决于promise对象的形状。我们将在本节结束时再次访问。
  • 如上所述,section 2.3.3.3的描述比实际代码长得多。这是因为巧妙地破解了promise = deferred(promise),使我们能够重用deferred函数的逻辑。这样可以确保promise.resolvepromise.reject在两者之间只能调用一次。我们只需要对deferred函数做些小改动就可以使该hack工作。

  • function deferred(promise) {
    var call = true;

    promise = promise || {
    then: undefined,
    ...
    };

    return /* the same object as before */
    }
    promise 状态和 then方法
    我们已经将决定 promise 对象的形状的问题推迟了很长时间,但是我们不能再延迟了,因为 fulfillreject函数的实现都依赖于它。现在该阅读规范对 promise 状态要说的内容了:

    A promise must be in one of three states: pending, fulfilled, or rejected.

    1. When pending, a promise:
      1. may transition to either the fulfilled or rejected state.
    2. When fulfilled, a promise:
      1. must not transition to any other state.
      2. must have a value, which must not change.
    3. When rejected, a promise:
      1. must not transition to any other state.
      2. must have a reason, which must not change.

    Here, “must not change” means immutable identity (i.e. ===), but does not imply deep immutability.


    我们如何知道 promise 目前处于哪个状态?我们可以做这样的事情:
    var PENDING   = 0;
    var FULFILLED = 1;
    var REJECTED = 2;

    var promise = {
    then: function (onFulfilled, onRejected) { ... },
    state: PENDING | FULFILLED | REJECTED, // vertical bar is not bitwise or
    ...
    };
    但是,还有更好的选择。由于 promise 状态只能通过其 then方法观察到(即,根据 promise 状态, then方法的行为有所不同),我们可以创建与这三种状态相对应的三个专门的 then函数:
    var promise = {
    then: pending | fulfilled | rejected,
    ...
    };

    function pending(onFulfilled, onRejected) { ... }
    function fulfilled(onFulfilled, onRejected) { ... }
    function rejected(onFulfilled, onRejected) { ... }
    另外,我们还需要一个属性来保存 promise 的数据。当 promise 待定时,数据是 onFulfilledonRejected回调的队列。当 promise 兑现时,数据就是 promise 的值(value)。当 promise 被拒绝时,数据就是 promise 的原因。
    当我们创建一个新的Promise时,初始状态为待定,初始数据为空队列。因此,我们可以完成 deferred函数的实现,如下所示:
    function deferred(promise) {
    var call = true;

    promise = promise || {
    then: pending,
    data: []
    };

    return /* the same object as before */
    }
    另外,既然我们知道了promise对象的形状,那么我们终于可以实现 fulfillreject函数:
    function fulfill(promise, value) {
    setTimeout(send, 0, promise.data, "onFulfilled", value);
    promise.then = fulfilled;
    promise.data = value;
    }

    function reject(promise, reason) {
    setTimeout(send, 0, promise.data, "onRejected", reason);
    promise.then = rejected;
    promise.data = reason;
    }

    function send(queue, callback, data) {
    for (var item of queue) item[callback](data);
    }
    我们需要使用 setTimeout,因为根据规范 section 2.2.4,在执行上下文堆栈仅包含平台代码之前,不得调用 onFulfilledonRejected
    接下来,我们需要实现 pendingfulfilledrejected函数。我们将从 pending函数开始,该函数将 onFulfilledonRejected回调推入队列并返回新的promise:
    function pending(onFulfilled, onRejected) {
    var future = deferred();

    this.data.push({
    onFulfilled: typeof onFulfilled === "function" ?
    compose(future, onFulfilled) : future.resolve,
    onRejected: typeof onRejected === "function" ?
    compose(future, onRejected) : future.reject
    });

    return future.promise;
    }

    function compose(future, fun) {
    return function (data) {
    try {
    future.resolve(fun(data));
    } catch (reason) {
    future.reject(reason);
    }
    };
    }
    我们需要测试 onFulfilledonRejected是否为函数,因为根据规范的 section 2.2.1,它们是可选参数。如果提供了 onFulfilledonRejected,则根据规范的 section 2.2.7.1section 2.2.7.2将它们与递延值组成。否则,它们会按照规范的 section 2.2.7.3section 2.2.7.4短路。
    最后,我们实现 fulfilledrejected函数,如下所示:
    function fulfilled(onFulfilled, onRejected) {
    return bind(this, onFulfilled);
    }

    function rejected(onFulfilled, onRejected) {
    return bind(this, onRejected);
    }

    function bind(promise, fun) {
    if (typeof fun !== "function") return promise;
    var future = deferred();
    setTimeout(compose(future, fun), 0, promise.data);
    return future.promise;
    }
    有趣的是,可以在上面恰当命名的 bind函数中看到 promises are monads。至此,我们对Promises / A +规范的实现已完成。
    优化 resolve 规范的 Section 2.3.2描述了当 resolve(promise, x)被确定为一个 promise 时对 x函数的优化。这是优化的 resolve函数:
    function resolve(promise, x) {
    if (x === promise) return reject(promise, new TypeError("Self resolve"));

    var type = typeof x;
    if (type !== "object" && type !== "function" || x === null)
    return fulfill(promise, x);

    try {
    var then = x.then;
    } catch (e) {
    return reject(promise, e);
    }

    if (typeof then !== "function") return fulfill(promise, x);
    // 2.3.2.1. If x is pending, promise must remain pending until x is
    // fulfilled or rejected.
    if (then === pending) return void x.data.push({
    onFulfilled: function (value) {
    fulfill(promise, value);
    },
    onRejected: function (reason) {
    reject(promise, reason);
    }
    });
    // 2.3.2.2. If/when x is fulfilled, fulfill promise with the same value.
    if (then === fulfilled) return fulfill(promise, x.data);
    // 2.3.2.3. If/when x is rejected, reject promise with the same reason.
    if (then === rejected) return reject(promise, x.data);

    promise = deferred(promise);

    try {
    then.call(x, promise.resolve, promise.reject);
    } catch (e) {
    promise.reject(e);
    }
    }
    全部放在一起
    该代码以 gist的形式提供。您可以简单地下载它并运行测试套件:
    $ npm install promises-aplus-tests -g
    $ promises-aplus-tests promise.js
    不用说,所有测试都通过了。

    关于javascript - 了解Promises/A +规范,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36192728/

    30 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com