问题描述
我的问题
我在网站上查看许多其他问题和答案时所尝试的操作
使用Promise等待一个API请求
const https = require("https");
function myRequest(param) {
const options = {
host: "api.xxx.io",port: 443,path: "/custom/path/"+param,method: "GET"
}
return new Promise(function(resolve,reject) {
https.request(options,function(result) {
let str = "";
result.on('data',function(chunk) {str += chunk;});
result.on('end',function() {resolve(JSON.parse(str));});
result.on('error',function(err) {console.log("Error: ",err);});
}).end();
});
};
使用Promise.all来完成所有请求并等待它们完成
const params = [{item: "param0"},...,{item: "param1000+"}]; // imagine 1000+ items
const promises = [];
base.map(function(params){
promises.push(myRequest(params.item));
});
result = Promise.all(promises).then(function(data) {
// doing some funky stuff with dat
});
到目前为止,还不错
当我将API请求的数量限制为最多10个时,它起作用,因为速率限制器会启动。当我 console.log(promises)时,它会返回一个数组'request '。
我试图在不同的地方添加setTimeout,例如:
...
base.map(function(params){
promises.push(setTimeout(function() {
myRequest(params.item);
},100));
});
...
但这似乎不起作用。当我 console.log(promises)时,它会返回一系列“函数”
我的问题
- 现在我被困住了...有什么想法吗?
- 当API出现错误时,我如何构建重试
感谢您的阅读,您已经是我书中的英雄!
解决方法
当您使用async / await进行复杂的控制流时,有助于弄清流的逻辑。
让我们从以下简单算法开始,将所有内容限制为每秒10个请求:
make 10 requests
wait 1 second
repeat until no more requests
为此,以下简单的实现将起作用:
async function rateLimitedRequests (params) {
let results = [];
while (params.length > 0) {
let batch = [];
for (i=0; i<10; i++) {
let thisParam = params.pop();
if (thisParam) { // use shift instead
batch.push(myRequest(thisParam.item)); // of pop if you want
} // to process in the
// original order.
}
results = results.concat(await Promise.all(batch));
await delayOneSecond();
}
return results;
}
现在,我们只需要实现一秒钟的延迟。我们可以为此简单地设置setTimeout:
function delayOneSecond() {
return new Promise(ok => setTimeout(ok,1000));
}
这无疑将为您提供每秒仅10个请求的速率限制器。实际上,它的执行速度要慢一些,因为每个批处理将在请求时间+一秒内执行。完全可以满足您的初衷,但是我们可以改进此方法以挤压更多请求,以尽可能接近每秒精确地10个请求。
我们可以尝试以下算法:
remember the start time
make 10 requests
compare end time with start time
delay one second minus request time
repeat until no more requests
同样,我们可以使用与上面的简单代码几乎完全相同的逻辑,但只需对其进行调整即可进行时间计算:
const ONE_SECOND = 1000;
async function rateLimitedRequests (params) {
let results = [];
while (params.length > 0) {
let batch = [];
let startTime = Date.now();
for (i=0; i<10; i++) {
let thisParam = params.pop();
if (thisParam) {
batch.push(myRequest(thisParam.item));
}
}
results = results.concat(await Promise.all(batch));
let endTime = Date.now();
let requestTime = endTime - startTime;
let delayTime = ONE_SECOND - requestTime;
if (delayTime > 0) {
await delay(delayTime);
}
}
return results;
}
现在,我们可以编写一个接受延迟时间的函数,而不是对一秒的延迟函数进行硬编码:
function delay(milliseconds) {
return new Promise(ok => setTimeout(ok,milliseconds));
}
我们这里有一个简单易懂的功能,它将速率限制为尽可能接近每秒10个请求。它相当突发,因为它在每个一秒钟的周期开始时发出10个并行请求,但它可以工作。当然,我们可以继续实施更复杂的算法来平滑请求模式等,但我将其留给您的创造力以及作为读者的功课。