我有几个项目需要查询第三方API,并且API的调用限制为每秒5次调用。我需要以某种方式限制我对API的调用,每秒最多5次调用。
到目前为止,我刚刚在一系列promises中使用了Promise.all()
,其中每个promise向API发送请求,并在API响应HTTP状态代码200
时解析,并在响应其他状态代码时拒绝。但是,当我在阵列中有超过5个项目时,我冒险Promise.all()
拒绝。
如何将Promise.all()
呼叫限制为每秒5次呼叫?
我希望这会对你有所帮助。
并且还要说这将使用Promise.all
来解决所有请求,如果你有一个大的查询列表,这将等待所有解决,并可能导致很多等待你的代码获得所有响应。如果其中一个请求拒绝,Promise.all
将拒绝。
我建议如果你不需要所有的结果,最好使用其他东西,如lodash debounce或throttle或处理这个的框架。
let items = [
{name: 'item1'},
{name: 'item2'},
{name: 'item3'},
{name: 'item4'},
{name: 'item5'},
{name: 'item6'}
];
// This is the api request that you send and return a promise
function apiCall(item) {
return new Promise((resolve) => {
setTimeout(() => resolve(item.name), 1000);
})
}
new Promise((resolve) => {
let results = [];
function sendReq (itemsList, iterate, apiCall) {
setTimeout(() => {
// slice itemsList to send request according to the api limit
let slicedArray = itemsList.slice(iterate * 5, (iterate * 5 + 5));
result = slicedArray.map(item => apiCall(item));
results = [...results, ...result];
// This will resolve the promise when reaches to the last iteration
if (iterate === Math.ceil(items.length / 5) - 1) {
resolve(results);
}
}, (1000 * iterate)); // every 1000ms runs (api limit of one second)
}
// This will make iteration to split array (requests) to chunks of five items
for (i = 0; i < Math.ceil(items.length / 5); i++) {
sendReq(items, i, apiCall);
}
}).then(Promise.all.bind(Promise)).then(console.log);
// Use Promise.all to wait for all requests to resolve
// To use it this way binding is required
如果你不太担心按顺序解析promises,你可以在bluebird中使用并发选项。
以下将仅处理5个查询。
const Promise = require('bluebird');
const buildQueries = (count) => {
let queries = [];
for(let i = 0; i < count; i++) {
queries.push({user: i});
};
return queries;
};
const apiCall = (item) => {
return new Promise(async (resolve, reject) => {
await Promise.delay(1000);
resolve(item.user);
});
};
const queries = buildQueries(20);
Promise.map(queries, async query => {
console.log( await apiCall(query) );
}, {concurrency: 5});