承诺的顺序调用

Sequential call of promises

我对 promises/functions 的调用顺序有疑问。基本上我获取一个 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

好的,我明白它为什么这样做了。首先,我将解释原因,然后讨论可能的更改。

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

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

因此,您会看到所有按 ID 进行的提取,它们都会同步解析,这会将所有保存操作排队到 nextTick 的 运行。这样你就得到了你看到的实际结果。

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

如果您真的想将其更改为 [=44= 获取]、[=44= 获取]、[=44= 获取],我可以想出一个很好的结构方式,但是必然会对所有操作进行排序,并且需要更长的时间才能完成(没有并行网络操作)。

仅供参考,出于测试目的,我将您的代码放入 运行ning 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的数据,然后保存等等。