Javascript 使用Node.js和Promises实现指数退避

Javascript 使用Node.js和Promises实现指数退避,javascript,node.js,Javascript,Node.js,我在代码中对一个API发出了大约70个请求。我得到了一个错误响应,告诉我我一个接一个的请求太快了,我决定使用指数退避的思想来解决这个问题 目前,我的代码是这样的: let backoffTime = 1; for (let i = 0; i < fileIds.length; i++) { let fileId = fileIds[i]; getFileName(fileId, auth) .then((name) => { // do

我在代码中对一个API发出了大约70个请求。我得到了一个错误响应,告诉我我一个接一个的请求太快了,我决定使用指数退避的思想来解决这个问题

目前,我的代码是这样的:

  let backoffTime = 1;

  for (let i = 0; i < fileIds.length; i++) {
    let fileId = fileIds[i];
    getFileName(fileId, auth)
    .then((name) => {
      // do some work
    })
    .catch((err) => {
      // assumes that the error is "request made too soon"
      backoff(backoffTime);
      backoffTime *= 2;
      i--;
      console.log(err);
    });
  }

function backoff(time) {
  let milliseconds = time * 1000;
  let start = (new Date()).getTime();
  while (((new Date()).getTime() - start) < milliseconds) {
    // do nothing
  }
}
let backoffTime=1;
for(设i=0;i{
//做些工作
})
.catch((错误)=>{
//假设错误为“请求太快”
退避(退避时间);
退避时间*=2;
我--;
控制台日志(err);
});
}
功能回退(时间){
毫秒=时间*1000;
让开始=(新日期()).getTime();
而(((新日期()).getTime()-start)<毫秒){
//无所事事
}
}
My
getFileName
函数向API发出请求并返回承诺

目前这不起作用,因为承诺是异步的(有点)。我的for循环运行得非常快,并调用getFileName函数,这使得那些API请求非常快。然后,它会得到一些API调用的错误,在这种情况下,它会更新backoffTime。这个实现不起作用


你知道我如何正确地实现这一点吗?

首先,用一个几乎无限的循环阻塞浏览器是一个非常非常糟糕的主意,只要使用承诺:

 const delay = ms => new Promise(res => setTimeout(res, ms));
然后,在继续循环和使用延迟之前,只需等待承诺:

 (async function() {
   for (let i = 0; i < fileIds.length; i++) {
     let fileId = fileIds[i];
     await getFileName(fileId, auth)
      .then((name) => {
        // do some work
      })
      .catch((err) => {
        // assumes that the error is "request made too soon"
        backoffTime *= 2;
        i--;
        console.log(err);
        return delay(backoffTime);
      });
   }
})();
(异步函数(){
for(设i=0;i{
//做些工作
})
.catch((错误)=>{
//假设错误为“请求太快”
退避时间*=2;
我--;
控制台日志(err);
返回延迟(回退时间);
});
}
})();

最简单的方法是使用async/await,然后等待每个请求,或者如果请求太慢,则创建包含15个请求和承诺的块。所有这些块

您还可以使用以下选项:

将承诺转换为回调需要一些额外的工作,反之亦然,但它会做得最好

这是函数:
parallelLimit(任务、限制、回调)

const tasks=[];
//这个for循环只是将任务准备到函数数组中
for(设i=0;idoWorkThatReturnsPromise(fileid[i])
.然后(val=>回调(null,val))
.catch(callback));
}
async.parallelLimit(任务,15,(错误,值)=>{
//做完后做点什么
})
您可以使用以下示例修复此问题:

for(设i=0;i{
//做些工作
})
.catch((错误)=>{
设置超时(()=>{
doCall(fileId,(backoffTime*2));
},退避时间*1000);
});

}
您可以使用以下方法修复此问题:我认为OP希望并行运行调用,并在延迟后重新运行失败的调用。您的示例按顺序运行所有调用。您的
async
await
是无用的。顺便说一句,您应该使用
async
await
尝试
捕获
或删除它们。他们不是。但是格式很差。。。混合
。然后
等待
可能会让人困惑,就像约翰斯·威尔姆斯在他的回答中说的那样:用一个几乎无限的循环阻塞浏览器是一个非常非常糟糕的主意!这需要在火灾中死去。永远,永远,永远,永远不要像这样使用无限循环。它会影响服务器,并且在等待时不会让其他代码运行。请使用冻结主线程来执行此操作。@UpTheCreek我无法删除它,因为它是可接受的答案。提及为什么应该删除它可能对其他人有用。顺便说一句,在我编辑它之前,我得到了大多数(如果不是全部的话)的反对票,这是因为在编辑之前,我的解决方案仍然包括一个阻塞主线程的
,而
循环。
const tasks = [];
// This for-cycle will just prepare the tasks into array of functions
for (let i = 0; i < fileIds.length; i++) {
  tasks.push(callback => doWorkThatReturnsPromise(fileIds[i])
     .then(val => callback(null, val))
     .catch(callback));
}

async.parallelLimit(tasks, 15, (err, values) => {
  // do something after it finishes
})