【问题标题】:Sequential call of promises承诺的顺序调用
【发布时间】:2015-12-22 17:01:29
【问题描述】:

我对调用承诺/函数的顺序有疑问。基本上我获取一个 ID 数组,然后对于每个 ID,想要获取订单详细信息并保存。然后到下一个ID。截至目前,在获取每个订单详细信息之前,它不会保存。

代码:

// Convenience function
var fetchOrders = function() {
  return self.fetchOrderList()
    .then(function(orders) {
      return P.map(orders, function(r) {
        return self.fetchOrderById(r.id).then(fn);
      });
    });
};

// Fetch initial order list
var fetchOrderList = function() {
  return new P(function(resolve, reject) {
    debug('Fetching initial list');
    resolve([{
      id: 123
    }, {
      id: 134
    }, {
      id: 1333
    }]);
  });
};


var fetchOrderById = function(id) {
  return new P(function(resolve, reject) {
    debug('Fetching by ID');
    resolve({
      foo: 'bar'
    });
  }).then(function(o) {
    debug('Saving');
    return o;
  })
};

fetchOrders();

预期结果:

Fetching initial list
Fetching by ID 
Saving
Fetching by ID 
Saving
Fetching by ID 
Saving

实际结果

Fetching initial list
Fetching by ID 
Fetching by ID 
Fetching by ID 
Saving
Saving
Saving

【问题讨论】:

  • 请描述所需的执行顺序。您首先获得一个 orderID 列表,然后您必须获取每个的详细信息并将该详细信息保存到数据库中。您希望哪些操作并行运行,哪些操作按顺序运行?而且,你提到了节流——你想要节流什么(这通常意味着放慢速度)?而且,你为什么要在doCall() 内部重新分配queue,但从未使用过重新分配的变量——这有什么意义?而且,延迟的意义何在?与其描述你的代码(这对我来说似乎过于复杂),不如描述一个你想要的简单规范。
  • 队列工作正常 - 每次调用 doCall 都应该通过队列。期望如上所列。 1)获取ID数组(排队)。 2) 依次对每个 id 进行排队呼叫以获取详细信息并将详细信息保存到 db(虚拟呼叫)。这不是正在发生的事情。在初始订单数组中的每个条目都已获取详细信息之前,它不会执行 db 调用。
  • 抱歉,您可能很容易看到自己想要什么,而且这听起来像是一个很有趣的问题,但老实说,我不明白您想要什么。我想在不参考现有代码的情况下查看您想要的确切结果的规范描述。
  • @jfriend00 - 全部清理干净 - 队列结果与问题无关。

标签: javascript promise bluebird


【解决方案1】:

好的,我明白为什么它正在做它正在做的事情。首先,我将解释原因,然后讨论可能的更改。

在您的测试代码中并且没有为P.map() 设置选项,它将一个接一个地同步调用所有fetchOrderById() 操作,因此它们将同时运行(如果它们是真正的异步操作)。

因此,您已经为所有订单启动了所有按 ID 提取操作。然后,随着每个解决方案的解决,他们将运行保存。在测试代​​码中,它们都立即解析。但是,根据 Promise 规范,所有 .then() 处理程序在所有同步代码都已展开并完成后被异步调用(从技术上讲,我认为它使用类似 nextTick() 的东西来调度每个 .then() 处理程序)。

因此,您会看到所有按 ID 获取的操作都发生了,它们都被同步解析,这会将所有保存操作排入队列以在 nextTick 处运行。这样你就得到了你所看到的实际结果。

如果 fetchByOrderId() 实际上是异步的(稍后解决),您仍然会看到所有的提取都立即开始 - 所有这些请求都将被启动,然后在每个 id 提取到达时进行保存。

如果您真的想将其更改为 Fetch by ID/save、Fetch by ID/save、Fetch by ID/save,我可以想到一个很好的结构方法,但这必然会对所有操作进行排序并采取完成时间更长(没有并行网络操作)。

仅供参考,我将您的代码放入正在运行的 jsFiddle 中用于测试目的:http://jsfiddle.net/jfriend00/k49qbd3z/。我只需要删除一个.then(fn),因为您提供的代码中没有这样的fn。


您可以通过将{concurrency: 1} 添加到P.map() 来强制它一次只执行一项操作来获得请求的输出。

// Convenience function
var fetchOrders = function() {
  return fetchOrderList().then(function(orders) {
      return P.map(orders, function(r) {
        return fetchOrderById(r.id);
      }, {concurrency: 1});
    });
};

在此处查看工作演示:http://jsfiddle.net/jfriend00/wshLu0g5/ 与此输出:

Fetching initial list
Fetching by ID
Saving
Fetching by ID
Saving
Fetching by ID
Saving

请记住,这可能会严重减慢最终结果,因为您正在序列化所有按 id 获取的内容,并且一次只保存一个操作。获取一个 id 的数据,然后保存,然后获取下一个 id 的数据,然后保存,然后获取下一个 id 的数据,然后保存,依此类推。

【讨论】:

  • 添加了获得所需输出顺序的解决方案。
猜你喜欢
  • 1970-01-01
  • 2013-05-09
  • 2021-09-08
  • 2019-06-16
  • 1970-01-01
  • 2016-11-10
  • 1970-01-01
  • 2017-08-18
  • 1970-01-01
相关资源
最近更新 更多