延迟Promise.all序列

时间:2018-05-05 10:10:55

标签: javascript node.js

我从外部API获取数据,并且在某些端点上出现以下错误:

reason: getaddrinfo EAI_AGAIN [the url]:443

我只能假设API的所有者有某种速率限制器,并且因为所有提取都会立即被阻止。

所以我试图在每次获取之间加一个延迟,但没有运气。这是我的尝试:

class someClass {
  ...
  dealyedFetch(url) {
    return new Promise ((resolve, reject) => {
      setTimeout(() => {
        fetch(url, (err, res) => { 
          if (err) return reject(err)
          return resolve(res)
        })
      }, 5000)    
    })
  }

  fetchUrls() {
    return Promise.all(
      this.urlList.map(url => {
        return this.dealyedFetch(url)
        .then(/* macht krieg */)
        .catch(err => ({err, url}))
      })
    )
  }    
}

以上不起作用,它似乎立即触发所有取出。请照亮我。

2 个答案:

答案 0 :(得分:2)

你立即将它们全部推迟,所以他们都等待5000毫秒,然后一起开火。

如果你想将它们串起来,你需要延长它们的延迟时间。您可以通过使用map中的索引将多个延迟(例如500毫秒)加倍来实现:

delayedFetch(url, delay) {                   // ***
  return new Promise ((resolve, reject) => {
    setTimeout(() => {
      fetch(url, (err, res) => { 
        if (err) return reject(err)
        return resolve(res)
      })
    }, delay)                                // ***
  })
}
fetchUrls() {
  return Promise.all(
    this.urlList.map((url, i) => {           // ***
      return this.delayedFetch(url, i * 500) // ***
      .then(/* macht krieg */)
      .catch(err => ({err, url}))
    })
  )
}    

第一个请求根本没有延迟,第二个请求是1 * 500 = 500毫秒,第三个请求是2 * 500 = 1000毫秒等。

旁注:在上文中,我修改了名称delayedFetch中的拼写错误(问题中dealyedFetchal被撤销了。)

附注2:上述内容解决了您实际问过的问题,但您可能会考虑一次只执行一次,在这种情况下this question's answers会告诉您如何执行此操作。或者可能是对优秀的并发限制,在这种情况下this question's answers会有所帮助。只是固定的延迟可能不是您的最佳解决方案。

答案 1 :(得分:0)

对于您的urlList,您正在同时做出一系列承诺。这些承诺中的每一个等待5秒,然后它们全部同时执行(5秒后)。

有关在串行中运行promises的规范SO帖子,请参阅此内容: Resolve promises one after another (i.e. in sequence)?

从那篇帖子中解脱出来:

function runSerial(tasks) {
  var result = Promise.resolve();
  tasks.forEach(task => {
    result = result.then(() => task());
  });
  return result;
}

然后你会通过用runSerial替换你的promise.all来消耗它。您可以通过一次释放5个url来进一步自定义逻辑,然后以串行方式运行批处理,但这应该可以帮助您开始。