为什么 Promise 构造函数需要一个 executor?

IT技术 javascript asynchronous promise w3c
2021-01-26 11:14:07

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

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


我认为 executor 函数应该是可选的,它的存在应该决定 promise 是否封装了解析。如果没有这样的要求,promise 会更加可扩展,因为您不必立即启动 async。Promise也应该是可重置的。这是一个 1 次开关,1 或 0,resolve()reject(). 有迹象表明,可附接的并行和串行结果众多:promise.then(parallel1)promise.then(parallel2)并且也promise.then(seq1).then(seq2)但参考特权玩家无法解析/拒绝到交换机

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

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

2个回答

这被称为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

它奏效了,但它有一个问题。Deferreds 和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 返回函数都包装在 a} catch (e) {和 a 中.catch(e =>以捕获所有错误。

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

此外,它还需要一个额外的类型,每个开发人员都必须了解 promise 构造函数没有的地方,这很好。

@Bergi 首先 - 我完全忘记了那个问题!第二 - 我认为这两者非常不同,因为问题本身是不同的 - 另一个是询问差异,这个只是关于新的 API 以及为什么它是这样设计的。不过,答案确实非常接近。
2021-03-20 11:14:07
这不是已经写在您对Defer().promise 和 Promise 之间的差异的回答中了吗?如果您没有回答,我可能会将其关闭为受骗者。
2021-03-23 11:14:07
解析 JSON 就是一个例子,因为它将throw同步进行,我们不希望它冒泡同步调用堆栈,而是希望将其作为Promise链的一部分。构造函数揭示了对象的内部不容易在别处暴露的部分——它是暴露的一面——没有什么是“透露的”。
2021-04-05 11:14:07
它没有透露有关构造函数的任何信息,您正在为构造函数提供一个函数来调用......
2021-04-06 11:14:07

仅供参考,如果您尽管有充分的理由反对延迟接口,但仍然渴望使用延迟接口而不是 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 的最佳方式。

在 MDN 上显示类似的内容