我正在用我的代码向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
}
}
我的getFileName
函数向API发出请求并返回Promise。
当前这不起作用,因为Promises是异步的(有点)。我的for循环运行得非常快,并调用了getFileName函数,这使这些API请求变得非常快。然后,它会收到某些API调用的错误,在这种情况下,它将更新backoffTime。此实现无效。
有什么想法可以正确实现吗?
答案 0 :(得分:2)
首先用几乎无限的循环来阻塞浏览器是一个非常糟糕的主意,只需使用promises:
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);
});
}
})();
答案 1 :(得分:1)
您可以使用closures来解决此问题,这是一个示例:
for (let i = 0; i < fileIds.length; i++) {
let fileId = fileIds[i];
doCall(fileId);
}
function backoff(time) {
let milliseconds = time * 1000;
let start = (new Date()).getTime();
while (((new Date()).getTime() - start) < milliseconds) {
// do nothing
}
}
function doCall(fileId, backoffTime = 1){
getFileName(fileId, auth)
.then((name) => {
// do some work
})
.catch((err) => {
backoff(backoffTime);
doCall(fileId, (backoffTime * 2))
});
}
此代码可能导致无限递归,您可能应该添加一些检查以防止这种情况。
答案 2 :(得分:0)
最简单的方法是使用async / await,然后等待每个请求,或者如果它对您来说太慢,则使用15个请求和Promise创建所有块。
您还可以使用以下命令:https://caolan.github.io/async/parallelLimit.js.html
将promise转换为回调,反之亦然,这将需要一些额外的工作,但它将做得最好。
这是函数:parallelLimit(tasks, limit, callback)
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
})
答案 3 :(得分:0)
您可以使用递归重试给定的时间,也可以吸收回复因子。
没有依赖项:
// pass the promise as the attempt property
const exponentialBackoff = ({
attempt,
maxAttempts = 3,
wait = 200,
factor = 2,
}) => new Promise((resolve, reject) => {
setTimeout(async () => {
try {
resolve(await attempt);
} catch (error) {
if (maxAttempts) {
exponentialBackoff({ attempt, maxAttempts: maxAttempts - 1, wait: wait * factor })
.then(resolve)
.catch(reject);
} else {
reject(error);
}
}
}, wait);
});
exponentialBackoff({ attempt: Promise.reject(new Error('Rejected')) })
.catch(err => window.alert(err.message));