【问题标题】:Possible contradiction between Promises/A+ spec and ECMAScript promises?Promises/A+ 规范和 ECMAScript 承诺之间可能存在矛盾?
【发布时间】:2021-05-26 16:04:31
【问题描述】:

assertedECMAScript 承诺是一个 Promises/A+ 实现,所以它们没有矛盾。但是,我遇到了一个据称不符合 Promises/A+ 的 ecma promises 行为。

当我们调用promise1.then(onFulfilled, onRejected) 来监听promise1 的输出时,我们得到另一个promise (promise2) 作为返回值。当执行所需的回调 (onFulfilled/onRejected) 并返回一些值 x 时,规范规定使用定义的 [[Resolve(promise2, x)]] 函数解决它。假设x恰好是一个promise本身(x === promise3),那么the steps必须取如下:

  • 如果x 是一个promise,采用它的状态:
  • 如果x 处于待处理状态,则promise2 必须保持待处理状态,直到x 被满足或拒绝。
  • 如果/当满足x 时,使用相同的值满足promise2
  • 如果/当x 被拒绝时,以同样的理由拒绝promise2

我想知道如果x 最终实现了另一个承诺 (promise4) 会怎样(没有任何阻碍,是吗?)。从规范摘录中可以得出结论,promise2 也必须用promise4 来满足。但在 ECMAScript 世界中似乎并非如此:

let promise4 = new Promise((resolve) => { resolve(4) })

let promise3 = new Promise((resolve) => {
    resolve(promise4);
});

let promise1 = new Promise((resolve) => {
    resolve(1);
});

let promise2 = promise1.then((val) => { return promise3 });
promise2.then(val => console.log(val)); // output: 4

换句话说,promise2promise4 的值实现。这种行为类似于规范中为其他thenable 对象定义的行为。那么 ECMAScript 的 promise 不进行预期的类型检查,只检查 x 是否有 then 方法?

【问题讨论】:

  • "...promise2 也必须满足 promise4。但在 ECMAScript 世界中似乎并非如此" - 正如您自己提到的输出console.log(val)4promise4 的“值”,那么问题出在哪里?
  • @Andreas promise3 (x) 满足 promise4 而不是 4。规范说promise2 必须满足x 的值而不是promise4 的值。
  • promise3promise4promise2promise3 -> console.log(val) 记录 4 promise4 的“值”
  • @Andreas 为什么promise3 的值是promise4 的值(即4)而不是promise4 本身?我的误解就在这里。你的意思是resolve(4)resolve(promise4)没有区别?但我认为它不是从规范中得出的。可能 ECMAScript resolve cb 不是实现我应该深入研究 ECMAScript 规范的承诺的直接方式..
  • "Promise.resolve() 方法返回一个使用给定值解析的Promise 对象。如果该值是一个承诺,则返回该承诺;如果该值是一个 thenable (即有一个"then" method),返回的promise将“跟随”那个thenable,采用它的最终状态;否则返回的promise将用该值实现。”

标签: javascript promise


【解决方案1】:

假设x 本身就是一个promise,那么必须采取的步骤如下:[…]

不,它们不需要被占用 - 只有当 x 是“承诺”时,它们才可能被占用。这些步骤是可选的(“允许”,而不是“必需”)优化:

Note 4:
通常,只有来自当前实现,才会知道x 是一个真正的承诺。该子句允许使用特定于实现的方法来采用已知符合承诺的状态。

ECMAScript 不会将其自己的Promises 视为“已知符合”,忽略这些步骤。他们只是像对待所有其他 thenables 一样对待原生 Promise。给定有no way to create an ECMAScript Promise that is fulfilled with another promise,相当于直接采用状态。

【讨论】:

  • 2.2.7.1 If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x). 也没有“必须”这个词,这是否意味着[[Resolve]] 是一个可选函数,并且实现可能会选择完全不同的方式来处理x
  • @Mergasov 不,then 方法必须遵循该算法,但它可以提供自己的 IsKnownPromise 检查。
  • 实际上,我倾向于将“允许”一词视为相关的手段(规范允许实现决定其IsKnownPromise 的底层内容),但不允许忽略我宁愿认为是强制性的所有这些步骤。不过,我得到了答案,ECMAScript 承诺的行为不同。
  • @Mergasov 同意。 ES6 定义IsKnownPromise = () => false 的决定很奇怪,并且阻止了许多有用的优化可能性。 Personally I consider it a bug,但到目前为止,似乎没有人关心。
【解决方案2】:

从规范摘录中可以得出结论,promise2 也必须与promise4 一起满足。

不,这不符合 Promises/A+ 规范。你引用的规则

  • 如果/当满足x 时,使用相同的值满足promise2

...本质上是递归的。应该更详细地理解如下:

如果/当满足x 时,使用与满足x 相同的值满足promise2

现在对于 x 实现的部分”,应用相同的解决过程(!):确实,正如 x 解决另一个 thenable (promise4),它又被锁定在链中的下一个承诺(promise4 在你的例子中)。这还不是满足的价值。 --resolving and fulfilling 之间有一个重要的区别。解析过程的第二次执行将确保x 满足 的值是promise4 满足的值。

锁定的 Promise 链可以有任意长度,但原则保持不变:每个都将通过这个 Promises/A+ 解决程序锁定到下一个 Promise 来解决。当这个链中的最后一个 fulfils (具有 non-thenable 值)时,所有被锁定的 Promise 都将得到 fulfills 这个值。

【讨论】:

  • 解析过程是命令式的,它是一个setter,而不是一个找到满足值的getter。你提到的递归反过来。 [[resolve]](promise, x) 的语句“x 的实现值实现promise”中没有递归 - 这是一个基本情况。当然,其他东西可能会运行[[resolve]](x, y),但这无关紧要。
  • 解析过程正向执行。我不建议通过 getter 找到履行值。它由链中的最后一个设置,影响所有锁定的 Promise。顺便说一句,Promises/A+ 规范说明了[[Resolve]](promise, thenable) 的递归性质”
  • 是的,通过您引用的条款,履行x 的一个承诺确实履行了与x 解决的其他承诺。但我看不到递归在哪里。承诺x 也可以通过任何其他方式解决,不仅通过承诺解决过程 - 它仅适用于从then() 调用创建的承诺。过程的递归性质仅指使用 thenable 进行解析(可能会使用另一个 thenable 参数调用回调)。
猜你喜欢
  • 1970-01-01
  • 2016-07-11
  • 2019-09-23
  • 2017-06-27
  • 1970-01-01
  • 2016-03-21
  • 2015-01-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多