简体   繁体   English

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

[英]Why does the Promise constructor need an executor?

When using Promises , why can't triggers for resolve and reject be defined elsewhere in the codebase?使用Promises 时,为什么不能在代码库的其他地方定义用于resolvereject触发器?

I don't understand why resolve and reject logic should be localized where the promise is declared.我不明白为什么应该在声明承诺的地方本地化resolvereject逻辑。 Is this an oversight, or is there a benefit to mandating the executor parameter?这是疏忽,还是强制executor参数有好处?


I believe the executor function should be optional, and that its existence should determine whether the promise encapsulates resolution or not.我认为 executor 函数应该是可选的,它的存在应该决定 promise 是否封装了解析。 The promise would be much more extensible without such mandates, since you don't have to initiate async right away.如果没有这样的要求,promise 将更具可扩展性,因为您不必立即启动 async。 The promise should also be resettable.承诺也应该是可重置的。 It's a 1 shot switch, 1 or 0, resolve() or reject() .这是一个 1 次切换,1 或 0, resolve()reject() There are a multitude of parallel and sequential outcomes that can be attached: promise.then(parallel1) and promise.then(parallel2) and also promise.then(seq1).then(seq2) but reference-privileged players cannot resolve/reject INTO the switch可以附加许多并行和顺序结果: promise.then(parallel1)promise.then(parallel2)以及promise.then(seq1).then(seq2)但参考特权玩家不能解决/拒绝 INTO开关

You can construct a tree of outcomes at a later time, but you can't alter them, nor can you alter the roots (input triggers)您可以稍后构建结果树,但您不能更改它们,也不能更改根(输入触发器)

Honestly, the tree of sequential outcomes should be edittable as well.. say you want to splice out one step and do something else instead, after you've declared many promise chains.老实说,顺序结果树也应该是可编辑的……假设您想在声明了许多承诺链之后,拼接出一个步骤并做其他事情。 It doesn't make sense to reconstruct the promise and every sequential function, especially since you can't even reject or destroy the promise either...重建承诺和每个顺序函数是没有意义的,特别是因为你甚至不能拒绝或破坏承诺......

This is called the revealing constructor pattern coined by Domenic.这被称为 Domenic 创造的揭示构造函数模式

Basically, the idea is to give you access to parts of an object while that object is not fully constructed yet.基本上,这个想法是给你访问对象的部分,而该对象没有完全构造呢。 Quoting Domenic:引用多梅尼克:

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.我将其称为揭示构造函数模式,因为 Promise 构造函数正在揭示其内部功能,但仅限于构造所讨论的 Promise 的代码。 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所以如果我们把 p 交给另一个消费者,说

The past过去

Initially, promises worked with deferred objects, this is true in the Twisted promises JavaScript promises originated in. This is still true (but often deprecated) in older implementations like Angular's $q , Q, jQuery and old versions of bluebird.最初,promise 与延迟对象一起工作,这在起源于 JavaScript 承诺的 Twisted 承诺中是正确的。在较旧的实现中仍然如此(但经常被弃用),例如 Angular 的$q 、Q、jQuery 和旧版本的 bluebird。

The API went something like: API 类似于:

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

It worked, but it had a problem.它奏效了,但它有一个问题。 Deferreds and the promise constructor are typically used for converting non-promise APIs to promises. Deferreds 和promise 构造函数通常用于将非promise API 转换为promise。 There is a "famous" problem in JavaScript called Zalgo - basically, it means that an API must be synchronous or asynchronous but never both at once. JavaScript 中有一个名为Zalgo的“著名”问题——基本上,这意味着 API 必须是同步或异步的,但不能同时存在。

The thing is - with deferreds it's possible to do something like:问题是 - 使用 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);
   });
}

There is a hidden subtle bug here - if param is not a valid JSON this function throws synchronously, meaning that I have to wrap every promise returning function in both a } catch (e) { and a .catch(e => to catch all errors.这里有一个隐藏的微妙错误 - 如果param不是有效的 JSON,则此函数同步抛出,这意味着我必须将每个 promise 返回函数都包装在} catch (e) {.catch(e =>以捕获所有错误。

The promise constructor catches such exceptions and converts them to rejections which means you never have to worry about synchronous exceptions vs asynchronous ones with promises.承诺构造函数捕获此类异常并将它们转换为拒绝,这意味着您永远不必担心同步异常与带有承诺的异步异常。 (It guards you on the other side by always executing then callbacks "in the next tick"). (它通过始终在“下一个滴答声”中执行then回调来保护您的另一边)。

In addition, it also required an extra type every developer has to learn about where the promise constructor does not which is pretty nice.此外,它还需要一个额外的类型,每个开发人员都必须了解 promise 构造函数没有的地方,这很好。

FYI, if you're dying to use the deferred interface rather than the Promise executor interface despite all the good reasons against the deferred interface, you can code one trivially once and then use it everywhere (personally I think it's a bad idea to code this way, but your volume of questions on this topic suggests you think differently, so here it is):仅供参考,如果您尽管有充分的理由反对延迟接口,但仍然渴望使用延迟接口而不是 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);
    }
}

Now, you can use the interface you seem to be asking for:现在,您可以使用您似乎要求的界面:

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(...)

Here a slightly more compact ES6 version:这是一个稍微紧凑的 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);
    }
}

Or, you can do what you asked for in your question using this Deferred() constructor:或者,您可以使用此Deferred()构造函数执行您在问题中要求的操作:

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

But, it has the downsides pointed out by Benjamin and is not considered the best way to code promises.但是,它有 Benjamin 指出的缺点,并且不被认为是编写 Promise 的最佳方式。

Something similar shown here on MDN . 在 MDN 上显示类似的内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM