Promise.defer 的正确模式是什么?

What's the correct pattern for Promise.defer?

我正在使用 TypeScript 和 async/await 来表示异步工作流。该工作流程的一部分是调用 Web Worker,并在它返回结果时继续。

在 C# 中,我会创建一个 TaskCompletionSourceawait 它的 Task 并且代码中的其他地方会调用 SetResult 来解析 TaskCompletionSource .

我可以在 JavaScript 中通过使用 Promise.defer()awaitPromise 和其他地方初始化 Deferrer 对象来做同样的事情,在 window.onmessage 侦听器将调用 resolve(或 reject)方法让异步工作流继续。

听起来不错,但是 MDN 说 defer 已经过时了。使用 Promise 构造函数的建议解决方案让委托完成工作并调用 resolve/reject 方法对我不起作用,因为我可能无法实现该逻辑,我只是想让对象在完全不同的词法范围内调用 resolvereject,我不能用那个函数来做到这一点。

有一个 Backwards and forwards compatible helper 通过绑定 resolvereject 函数为我提供了这样的对象,我可以在不更改代码语义的情况下使用这些函数。但这是一种不好的做法吗?是否有公认的更好的模式? JavaScript 中 TaskCompletionSource 的惯用等价物是什么?

只要您确定确实需要延期,那么延期的助手绝对没有错。在我所有的 promise 编程中,我只发现了一种情况,我实际上需要一个 deferred 而不能仅仅重组我的代码以使用典型的 Promise 构造函数模式。

我发现的情况和其他人指出的情况似乎发生在创建 promise/deferred 对象的代码与解析或拒绝它的代码之间分开时。通常,你把它们放在一起,但有时它们是完全不同的代码部分,并且有合乎逻辑的理由这样做。在这些情况下,基于常规承诺构建的延迟对象可能是更简洁的实现方法。

这是另一个简单的 Deferred 实现,它也是向后和向前兼容的:

But is this a bad practice?

我知道有些纯粹主义者认为你永远不应该这样做。但是,如果您确定这是实现代码的最干净、最简单的方法,并且使用典型的 Promise 构造函数执行器回调来实现它并不实用,那么我会说这是一个非常好的做法。有些人甚至想在不尝试或学习 promise constructor/executor 的情况下使用 deferred,这不是一个好的做法。出于各种原因,promise构造函数应该在实用的时候使用。

首选 promise 构造函数的一个重要原因是执行器回调函数中所有特定于 promise 的代码都是 "throw-safe"。如果在该代码中抛出异常,它将自动被捕获并拒绝承诺(这是一件好事)。一旦你使用了一个 deferred 并且有一堆代码在那个回调之外操纵 promise,它就不会自动抛出安全。事实上,您的代码甚至可以同步抛出异常,这对调用者来说是一场噩梦。您可以在此处查看对该问题的描述:。因此,延迟模式不是首选,但在适当的保护下,仍然有一些情况(通常很少见)会导致更清晰的实现。

Is there a recognized, better pattern?

和上面的答案差不多。首选是使用 promise constructor/executor,但在不切实际的情况下(通常是代码的不同部分创建 promise 然后 resolve/reject 它)并且没有简单的方法来重新组织代码为了与 promise constructor/executor 很好地协同工作,那么延迟对象可能是首选实现。你通常会发现这些情况很少见,因为大多数时候你可以构建你的代码,所以相同的代码块正在创建和 resolve/rejecting 承诺,你不需要延迟。

What's an idiomatic equivalent of TaskCompletionSource in JavaScript?

Javascript 中没有内置等效项。因此,您必须构建自己的 promises 似乎是一种自然的方式,因为它们自然而然地用于指示完成或错误。您必须向我们展示您的实际代码,以便我们就您的特定情况是否可以在不使用延迟对象的情况下干净地实现提出意见。