【问题标题】:How to use promises when a function returns an array of objects当函数返回对象数组时如何使用 Promise
【发布时间】:2016-04-10 08:36:48
【问题描述】:

我有一个不使用 Promises 的复杂 Node 应用程序,但我意识到我有一个需要解决的异步问题,Promises 似乎是一种可能的方法。不过,我没有使用 Promises 的经验,而且我发现学习曲线具有挑战性。

为了保持封装性和可测试性,应用程序的一部分与另一部分之间的主要接口是要执行的操作列表。这只是一个对象数组,其中包含调用函数用来执行操作的属性。因此,第一个模块更抽象,只是指定要采取的通用操作,而调用模块是针对特定后端 API 执行操作的具体实现。为了保持封装性和可测试性,通用模块需要不知道具体模块。

我遇到的问题是,现在需要由通用模块确定一系列异步步骤,在特定模块中执行的较早操作完成之前,后面的步骤不能发生。但是,由于通用模块无法了解特定模块,因此它本身无法执行操作。它所能做的就是将动作返回给特定的模块。

但具体模块需要执行动作,然后跟进通用模块确定的其他动作。

这就是我卡住的地方。我不知道如何从通用模块到特定模块返回动作对象列表以及它们的可操作。不知道是因为我对 Bluebird 缺乏了解,还是我想做一些 Bluebird 做不到的事情。

似乎一种方法是向动作添加 afterExecution 属性,这将是动作完成后要调用的后续函数。然而,这感觉很像本土的 Promises,所以我不愿意走这条路。

如果您仍然能够遵循这个令人困惑的场景,我要问的是如何返回一个操作列表,这些操作还具有在处理主要操作对象后要执行的“回调”函数。蓝鸟能应付吗?


编辑: 睡了一会儿,我希望能说明我所追求的。这是我目前实施的精简版。

generic.js(不知道specific.js):

function createActions() {
    var actions = []

    var action1 = {command:'buy', qty:100, symbol:'S', price:3.50}
    var self = this
    action1.afterwards = function() {
       self.setStopAndTarget()
    }
    actions.push(action1)
    // add some more actions

    return actions
}

specific.js

Promise.method(function checkForTrades() {
    var actions = generic.createActions()
    actions.forEach(function(a) {
        if( a.command === 'buy' ) {
            buyStock(a.symbol, a.qty, q.price).then(function(a) {
                if( a.afterwards ) {
                    a.afterwards()
                }
            })
        }
    })
})

所以,specific.js 调用 generic.js 中的createActions() 来获取操作列表。 actions 是一个 JSON 对象数组,但是,如您所见,我还向 action 添加了一个函数作为名为 afterwards 的属性。

specific.js 然后处理actions。在action 完成后,我使用Promise 检查属性afterwards 是否存在。如果存在,我调用它。

我真正想做的是将then() 编码在 generic.js 中,而不是 specific.js,这样我就没有将afterwards 属性创建为action 的一部分。但我不知道该怎么做。

我有什么工作,但我很确定 Promise 可以做到这一点,而无需将 afterwards 添加到 action

另外,如果返回一系列操作,我想知道如何推迟 then() 操作,直到所有操作都完成。我很确定这只是使用Promises.all 的问题,但看看语法会很有帮助。

谢谢!

【问题讨论】:

  • 在通过我阅读了两次之后,我发现这有点难以理解。示例代码甚至是图表?
  • 您说“应用程序的一部分”,然后您谈到“模块”——通过“模块”,您指的是这两个“部分”吗?谁在这里呼唤谁,谁又将什么归还给谁?
  • 我不能责怪任何很难遵循这一点的人。它是在漫长的日日夜夜之后太阳升起时写的。我将使用一些代码来编辑帖子,这些代码应该可以清除我所问的内容。我感谢您为破译我的不连贯性所做的努力。 :)

标签: javascript node.js promise bluebird


【解决方案1】:

我不确定我是否理解您的问题,但这里有一些想法。

如果您告诉我在进行下一步之前需要同步一组响应,您可以使用Promise.allPromise.join 来执行此操作。

它们之间的区别只是传递参数的方式。 Promise.all 接受一个 thenable 数组,而 Promise.join 接受一个 thenable 参数列表。

两者的工作方式相同:只有当所有输入的 Promise 都解决时,生成的 Promise 才会解决。如果其中任何一个被拒绝,则生成的 Promise 也将被拒绝。

因此,如果您有一组异步进程,您所要做的就是为每个进程创建一个 Promise,然后将这些 Promise 传递给 Promise.all

【讨论】:

    【解决方案2】:

    有几种可能与此相关的模式,具体取决于您要完成的任务的详细信息。

    也许最有希望的是一种类似协程的方法,使用 Promise 实现(使用 Promise 时,请不要忘记“回调”这个词)。

    在这种方法中,通用模块将调用特定模块中的函数,该函数在完成时返回一个承诺。然后,通用模块将其以下执行步骤从特定模块返回的承诺中挂起。这是一个非常基本的实现:

    function generic() {
      var specifics = [specific1, specific2, specific3];
      var promise = Promise.resolve();
      for (var i = 0; i < specifics.length; i++) {
        promise = promise.then(specifics[i]);
      }
      return promise;
    }
    
    function specific1() {
      return specific1_async_thing() . then(specific2_async_thing);
    }
    

    换句话说,specific1“回调”到泛型端的方式是向它返回一个承诺,让泛型端知道(通过then)何时执行其下一步。

    上述逻辑将按顺序执行通用步骤——还有其他方法适用于其他场景,比如让所有特定的人一起开始。

    考虑异步函数也可能对您有用。他们将允许将generic 函数重写为

    async function generic() {
      var specifics = [specific1, specific2, specific3];
      for (var i = 0; i < specifics.length; i++) {
        await specifics[i];
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2017-08-01
      • 2019-01-09
      • 2016-08-31
      • 1970-01-01
      • 1970-01-01
      • 2019-02-16
      • 2018-09-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多