总是返回承诺有用吗

Posted

技术标签:

【中文标题】总是返回承诺有用吗【英文标题】:Is it useful to always return a promise 【发布时间】:2014-04-23 19:13:41 【问题描述】:

我正在使用 bluebird 围绕 http 服务设计一些 nodejs api 包装器。 这个包装器中的许多函数都是异步的,因此从这些实现中返回 Promise 很有意义。

我的同事已经在这个项目上工作了几天,有趣的模式正在出现,他还从同步实现的函数中返回承诺。

例子:

function parseArray(someArray)
    var result;
    // synchronous implementation
    return Promise.resolve(result);           

如果以后需要使实现异步,我可以看到这将如何有用,因为您不必重构调用站点。 我想所有方法都始终“异步”也很好,但我不确定这到底有多棒。

这是否被认为是一种不好的做法,我们有什么理由不应该这样做吗?

【问题讨论】:

这看起来毫无用处的沉重。如有疑问,您始终可以cast 函数返回,我看不出有任何理由不提供直接可用的值。 顺便说一句,这不是一个主要基于意见的问题吗? 这实际上是一个很常见的反模式,我认为有这个问题很有用,因为它是一个反模式。 promise 的目的是让你的代码更简单、更清晰。如果使用 Promise 会让你在很多函数中添加一些无用的重复代码,那你就错了。 这样做会欺骗 API 的用户,让他们相信它是异步实现的 【参考方案1】:

在同步方法中返回一个承诺是没有意义的。

Promises 提供了对并发性的抽象。当不涉及并发时,例如提供数组时。返回一个 Promise 会导致更糟糕的流控制,并且速度要慢得多。

这也传达了错误的信息。事实上,无缘无故地承诺事情是一种很常见的反模式。

有用的一种情况是方法可能 是异步的 - 例如:从缓存中获取某些内容或在缓存中不存在时发出请求:

function getData(id)
     if(cache.has(id) return Promise.cast(cache.get(id));
     return AsyncService.fetch(id).tap(cache.put);

【讨论】:

相当慢”?良好的 Promise 实现应该几乎不会引起注意。 @Bergi 比同步执行?注意我是说它比同步执行慢得多,而不是形成回调:) 是的,一个好的 Promise 实现应该能够像同步执行一样快地处理这个问题,而且只需要一个恒定(而且很小)的开销。慢一点,是的,但不多。 @Vegaaaa no - 更明显的是避免异步编程的情况 - 如果您有 CPU 密集型工作,请使用网络工作者。 @Vegaaaa 不,有时它是必需的 - 建议只是在不需要时避免它。【参考方案2】:

如果我们可以忽略性能,那么只有在使用示例实现时才不好:

function parseArray(someArray) 
    var result;
    // synchronous implementation
    return Promise.resolve(result);           

这个函数很乱,因为它可以同步抛出但也返回一个promise。返回承诺的函数绝不能抛出 - 否则您将失去承诺的大部分好处,因为现在您必须同时使用 try-catch.catch()

正确的实现方式是用.method“注解”一个函数:

var parseArray = Promise.method(function()  
    var result;
    //Promise.resolve(result) is unnecessary now
    return result;
);

现在保证函数永远不会同步抛出,并且可以以一致的方式使用。

【讨论】:

请注意,Promise.method 非常酷,可以将抛出的异常转换为拒绝,但它是 Bluebird 特有的。 声明返回 promise 的函数永远不能抛出,这并不完全正确,例如如果提供的参数无效,任何函数都应该抛出,所有其他异步 API 都是这种情况,它不应该与 promises 不同。 @MariuszNowak“如果提供的参数无效,任何函数都应该抛出”我认为“无效”是一个非常挑剔的规范。如果函数需要执行异步调用来验证参数怎么办?如果函数至少有 1 个异步操作,那么抛出异步(拒绝)对我来说更有意义。 @WillemD'haeseleer 在语言级别无效,例如我们期望函数并获得 null,任何阻止我们初始化异步请求的东西。当然异步操作的任何错误结果肯定都必须通过promise来拒绝,但完全不同。 @MariuszNowak 函数返回承诺绝不能抛出。如果他们这样做了,它可能会以很多方式弄乱你的代码。当你有一个函数数组,或者你映射到一个函数列表时。在没有析构函数或任何理智的方式进行确定性资源管理的语言中 - 从执行异步操作的函数中抛出是非常危险的。如果你想从一个 Promise 中发出错误信号,你可以reject。否则,您需要两个 catch 子句。它在链中也不能很好地工作。

以上是关于总是返回承诺有用吗的主要内容,如果未能解决你的问题,请参考以下文章

你能在返回之前解决一个 angularjs 承诺吗?

从承诺返回 then()

复杂承诺返回链中的承诺 catch() 顺序

什么是everyauth 承诺?

你能“累积”承诺结果形成一连串“那么”吗? [复制]

从 vuex 存储操作中的承诺返回错误