为什么 Promise 构造函数需要一个执行器?

Why does the Promise constructor need an executor?

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

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


我认为executor函数应该是可选的,它的存在应该决定promise是否封装了resolution。如果没有这样的授权,承诺将更具可扩展性,因为您不必立即启动异步。承诺也应该是可重置的。这是一个单次切换,1 或 0,resolve()reject()。可以附加许多并行和顺序的结果:promise.then(parallel1)promise.then(parallel2) 以及 promise.then(seq1).then(seq2) 但参考特权玩家不能 resolve/reject 进入开关

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

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

这叫做 the revealing constructor pattern 多梅尼奇创造的

基本上,这个想法是让您在对象尚未完全构建时访问该对象的 部分 。引用多米尼奇的话:

I call this the revealing constructor pattern because the Promise constructor is revealing its internal capabilities, but only to the code that constructs the promise in question. The ability to resolve or reject the promise is only revealed to the constructing code, and is crucially not revealed to anyone using the promise. So if we hand off p to another consumer, say

过去

最初,promises 与延迟对象一起工作,这在 JavaScript promises 起源的 Twisted promises 中是正确的。在像 Angular 的 $q、Q、jQuery 和旧版本的蓝鸟。

API 是这样的:

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

成功了,但是有问题。 Deferred 和 promise 构造函数通常用于将非 promise API 转换为 promises。 JavaScript 中有一个名为 Zalgo 的 "famous" 问题 - 基本上,这意味着 API 必须是同步或异步的,但不能同时进行。

问题是 - 使用 deferreds 可以做类似的事情:

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 这个函数 throws 同步,这意味着我必须包装每个承诺在 } catch (e) {.catch(e => 中返回函数以捕获所有错误。

promise 构造函数捕获此类异常并将它们转换为拒绝,这意味着您永远不必担心同步异常与带有 promise 的异步异常。 (它通过始终执行 then 回调 "in the next tick" 在另一端保护你)。

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

仅供参考,如果你渴望使用延迟接口而不是 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