【问题标题】:What's the correct pattern for Promise.defer?Promise.defer 的正确模式是什么?
【发布时间】:2023-03-07 10:04:01
【问题描述】:

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

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

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

听起来不错,但 MDN 说 defer 已过时。使用 Promise 构造函数的建议解决方案让委托完成工作并调用 resolve/reject 方法对我不起作用,因为该逻辑可能超出我的能力范围,我只想要对象要在完全不同的词法范围内调用 resolvereject,我无法使用该函数来执行此操作。

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

【问题讨论】:

    标签: javascript c# asynchronous promise deferred


    【解决方案1】:

    只要你确定你真的需要一个 deferred,这个 helper 绝对没有错。在我所有的 Promise 编程中,我只发现了一种情况,我实际上需要一个 deferred 并且不能仅仅重构我的代码以使用典型的 Promise 构造函数模式。

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

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

    Why does the Promise constructor need an executor?

    但这是一种不好的做法吗?

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

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

    有公认的更好的模式吗?

    和上面的答案差不多。首选是使用 Promise 构造器/执行器,但在不实用的情况下(通常是不同的代码部分创建 Promise,然后解析/拒绝它)并且没有简单的方法来重新组织代码以使其正常工作承诺构造器/执行器,那么延迟对象可能是首选实现。您通常会发现这些情况很少见,因为大多数时候您可以构建代码,以便同一块代码创建和解析/拒绝承诺,而您不需要延迟。

    什么是 JavaScript 中 TaskCompletionSource 的惯用等价物?

    Javascript 中没有内置的等价物。因此,您必须构建自己的,并且似乎 Promise 是一种自然的方式,因为它们自然地构建以表示完成或错误。您必须向我们展示您的实际代码,以便我们就您的具体情况是否可以在不使用延迟对象的情况下干净地实现提供意见。

    【讨论】:

    • 这是一个了不起的答案,感谢您投入这么多时间。我无法想象如何在使用Promise 时创建一个Promise 以在主窗口中等待,并从一个web worker 中创建一个resolve(至少是一个不同的文件!)构造函数执行器,所以我认为我可以使用Deferred 来解决这个问题。
    • @TomášHübelbauer - 好吧,webWorker 最终将不得不向主线程发送一条消息以与之通信,对吧?您能否让承诺执行者监听该消息并让它在收到消息时解决执行者中的承诺?我只是在这里猜测,因为我没有看到你的真实代码。
    • 想一想,我可以在 executor 实现中收听消息,尽管目前我的消息处理器位于中心位置,这样我就不得不将它分解给每个 executor。我会玩弄它,也许我忽略了一些东西。如果我没有想法或不确定,将发布一个问题。 :)
    • @TomášHübelbauer - 通常(尽管并非总是)执行程序设计模式只需要重新考虑代码的结构以便使用它。祝你好运。
    • 我一直避免使用它,因为我在每个函数中都创建了一个callback 函数。 jshint 警告不要动态创建函数。这不是一个大的性能交易吗?这就是为什么只使用new Promise(callback) 的原因吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-21
    • 2017-11-16
    • 2023-03-21
    • 1970-01-01
    • 2013-03-03
    相关资源
    最近更新 更多