【问题标题】:Why does the Promise constructor need an executor?为什么 Promise 构造函数需要一个执行器?
【发布时间】:2016-10-05 17:28:06
【问题描述】:

使用Promises 时,为什么不能在代码库的其他地方定义resolvereject 的触发器?

我不明白为什么 resolvereject 逻辑应该在声明承诺的地方进行本地化。这是疏忽,还是强制使用 executor 参数有什么好处?


我认为 executor 函数应该是可选的,它的存在应该决定 Promise 是否封装了解析。如果没有这样的要求,promise 的可扩展性会更高,因为您不必立即启动 async。承诺也应该是可重置的。这是一个 1 次开关,1 或 0,resolve()reject()。可以附加许多并行和连续的结果:promise.then(parallel1)promise.then(parallel2) 以及 promise.then(seq1).then(seq2) 但参考特权玩家无法解决/拒绝进入开关

您可以稍后构建结果树,但不能更改它们,也不能更改根(输入触发器)

老实说,顺序结果树也应该是可编辑的。假设您想拼接出一个步骤并做其他事情,在您声明了许多承诺链之后。重构 Promise 和每个顺序函数是没有意义的,尤其是因为你甚至不能拒绝或破坏 Promise...

【问题讨论】:

  • 这就是语法的设计方式..
  • 旁注:不确定我是否会使用此 API 客户端,因为并非所有浏览器都支持它。
  • 您可能会发现这很有用:When would someone need to create a deferred 讨论了如何将执行器用于所有情况。还有Promises for promises that are yet to be created without using the deferred antipattern.
  • 解决或拒绝承诺的能力是一项特权功能,默认情况下,仅对创建承诺的代码可用。执行人就是这样设置的。如果执行器想要在闭包之外分配解析或拒绝处理程序,它可以这样做以使其他代码能够调用它。但是,默认情况下,它对 executor 是私有的,并且是故意这样做的。
  • 我看到有人说这是设计使然,但我不明白为什么要这样设计,这有什么好处?

标签: javascript asynchronous promise w3c


【解决方案1】:

这称为the revealing constructor pattern,由 Domenic 创造。

基本上,这个想法是让您在对象尚未完全构造时访问对象的部分。引用 Domenic:

我将其称为揭示构造函数模式,因为 Promise 构造函数揭示了它的内部功能,但仅限于构造相关 Promise 的代码。解决或拒绝 Promise 的能力只显示给构造代码,并且至关重要的是不会显示给使用 Promise 的任何人。所以如果我们把 p 交给另一个消费者,比如说

过去

最初,Promise 与延迟对象一起工作,这在 JavaScript Promise 起源的 Twisted Promise 中是正确的。这在 Angular 的 $q、Q、jQuery 和旧版本的 bluebird 等较旧的实现中仍然是正确的(但经常被弃用)。

API 类似于:

var d = Deferred();
d.resolve(); 
d.reject();
d.promise; // the actual promise

它工作,但它有一个问题。 Deferred 和 Promise 构造函数通常用于将非 Promise API 转换为 Promise。 JavaScript 中有一个名为Zalgo 的“著名”问题 - 基本上,这意味着 API 必须是同步或异步的,但不能同时两者兼而有之。

问题是 - 使用延迟可以执行以下操作:

function request(param) {
   var d = Deferred();
   var options = JSON.parse(param);
   d.ajax(function(err, value) { 
      if(err) d.reject(err);
      else d.resolve(value);
   });
}

这里有一个隐藏的细微错误 - 如果 param 不是有效的 JSON,则此函数会同步抛出,这意味着我必须将每个 promise 返回函数都包装在 } catch (e) {.catch(e => 捕获所有错误。

promise 构造函数捕获此类异常并将它们转换为拒绝,这意味着您不必担心同步异常与带有 promise 的异步异常。 (它通过“在下一个滴答声中”始终执行 then 回调来保护你)。

此外,它还需要一个额外的类型,每个开发人员都必须了解 Promise 构造函数在哪些方面没有这样做,这非常好。

【讨论】:

  • 这不是已经写在你对Difference between defer().promise and Promise的回答中了吗?如果你没有回答,我可能会把它当作骗子关闭。
  • @Bergi 首先 - 我完全忘记了这个问题!其次 - 我认为两者是完全不同的,因为问题本身是不同的 - 另一个是询问差异,这个只是关于新的 API 以及为什么它是这样设计的。答案确实非常接近。
  • 它没有透露有关构造函数的任何信息,您正在为构造函数提供调用的函数.. async 意味着随时可能发生某些事情,解析无效的 json 没有任何意义兑现承诺
  • 解析 JSON 就是一个例子,因为它会同步地throw,我们不希望它在同步调用堆栈中冒泡,而是希望它作为 Promise 链的一部分。构造函数揭示了对象的内部部分,这些部分不容易暴露在其他地方 - 它是揭示的一面 - 没有“揭示它”。
【解决方案2】:

仅供参考,如果您渴望使用延迟接口而不是 Promise 执行器接口,尽管有很多反对延迟接口的充分理由,您可以简单地编写一个代码,然后在任何地方使用它(我个人认为这是一个坏主意以这种方式编码,但是您在此主题上的大量问题表明您的想法不同,所以在这里):

function Deferred() {
    var self = this;
    var p = this.promise = new Promise(function(resolve, reject) {
        self.resolve = resolve;
        self.reject = reject;
    });
    this.then = p.then.bind(p);
    this.catch = p.catch.bind(p);
    if (p.finally) {
        this.finally = p.finally.bind(p);
    }
}

现在,您可以使用您似乎要求的界面:

var d = new Deferred();
d.resolve(); 
d.reject();
d.promise;     // the actual promise
d.then(...)    // can use .then() on either the Deferred or the Promise
d.promise.then(...)

这里有一个更紧凑的 ES6 版本:

function Deferred() {
    const p = this.promise = new Promise((resolve, reject) => {
        this.resolve = resolve;
        this.reject = reject;
    });
    this.then = p.then.bind(p);
    this.catch = p.catch.bind(p);
    if (p.finally) {
        this.finally = p.finally.bind(p);
    }
}

或者,您可以使用此Deferred() 构造函数执行您在问题中要求的操作:

var request = new Deferred();
request.resolve();
request.then(handleSuccess, handleError);

但是,它有 Benjamin 指出的缺点,并且不被认为是编写 Promise 的最佳方式。

类似的东西显示here on MDN

【讨论】:

    猜你喜欢
    • 2012-12-25
    • 1970-01-01
    • 2023-04-09
    • 2023-04-08
    • 2015-09-28
    • 1970-01-01
    • 1970-01-01
    • 2020-03-10
    • 2013-09-18
    相关资源
    最近更新 更多