我有一个返回promise的方法,并且在内部该方法调用API,每分钟只能有20个请求。问题是我有大量的对象(大约300个),我想为每个对象调用API。
目前我有以下代码:
const bigArray = [.....];
Promise.all(bigArray.map(apiFetch)).then((data) => {
...
});
但它没有处理时间限制。我希望我可以使用来自lodash
的_.chunk和_.debounce之类的东西,但我无法绕过它。谁能帮助我?
答案 0 :(得分:4)
如果你可以使用Bluebird promise库,它内置了一个并发功能,可以让你在一次飞行中管理一组异步操作,最多只有N个。
var Promise = require('bluebird');
const bigArray = [....];
Promise.map(bigArray, apiFetch, {concurrency: 20}).then(function(data) {
// all done here
});
这个界面的好处在于它将保留20个飞行请求。它将启动20,然后每次完成,它将启动另一个。所以,这比发送20,等待所有人完成,发送20多个等等更有效...
这也以与bigArray
完全相同的顺序提供结果,因此您可以确定哪个结果与哪个请求相关。
当然,您可以使用计数器使用通用承诺自行编码,但由于它已经在Bluebird库中构建,我认为我会推荐这种方式。
Async库也有类似的并发控制,虽然它显然不是基于承诺的。
这是一个只使用ES6承诺的手工编码版本,它保持结果顺序并始终保持20个飞行请求(直到没有剩余20个)以获得最大吞吐量:
function pMap(array, fn, limit) {
return new Promise(function(resolve, reject) {
var index = 0, cnt = 0, stop = false, results = new Array(array.length);
function run() {
while (!stop && index < array.length && cnt < limit) {
(function(i) {
++cnt;
++index;
fn(array[i]).then(function(data) {
results[i] = data;
--cnt;
// see if we are done or should run more requests
if (cnt === 0 && index === array.length) {
resolve(results);
} else {
run();
}
}, function(err) {
// set stop flag so no more requests will be sent
stop = true;
--cnt;
reject(err);
});
})(index);
}
}
run();
});
}
pMap(bigArray, apiFetch, 20).then(function(data) {
// all done here
}, function(err) {
// error here
});
答案 1 :(得分:1)
您可以每分钟发送1个20个请求,或者每3秒发出1个请求(后者可能是API所有者首选)。
function rateLimitedRequests(array, chunkSize) {
var delay = 3000 * chunkSize;
var remaining = array.length;
var promises = [];
var addPromises = function(newPromises) {
Array.prototype.push.apply(promises, newPromises);
if (remaining -= newPromises.length == 0) {
Promise.all(promises).then((data) => {
... // do your thing
});
}
};
(function request() {
addPromises(array.splice(0, chunkSize).map(apiFetch));
if (array.length) {
setTimeout(request, delay);
}
})();
}
每3秒拨打一次电话:
rateLimitedRequests(bigArray, 1);
每分钟20次:
rateLimitedRequests(bigArray, 20);
如果您更喜欢使用_.chunk
和 1 _.debounce
_.throttle
:
function rateLimitedRequests(array, chunkSize) {
var delay = 3000 * chunkSize;
var remaining = array.length;
var promises = [];
var addPromises = function(newPromises) {
Array.prototype.push.apply(promises, newPromises);
if (remaining -= newPromises.length == 0) {
Promise.all(promises).then((data) => {
... // do your thing
});
}
};
var chunks = _.chunk(array, chunkSize);
var throttledFn = _.throttle(function() {
addPromises(chunks.pop().map(apiFetch));
}, delay, {leading: true});
for (var i = 0; i < chunks.length; i++) {
throttledFn();
}
}
1 您可能需要_.throttle
,因为它在延迟后执行每个函数调用,而_.debounce
将多个调用分组到一个调用中。请参阅article
去抖动:将其视为&#34;将多个事件分组在一个&#34;中。想象一下,你回家,进入电梯,门正在关闭......突然你的邻居出现在大厅里,试图跳上电梯。讲礼貌!并为他打开门:你正在解除电梯的离开。考虑到同样的情况可能会再次发生在第三个人身上,等等......可能会在几分钟内推迟出发。
节气门:将其视为阀门,它可以调节执行的流量。我们可以确定在特定时间内调用函数的最大次数。所以在电梯类比中......你很有礼貌地让人们进入10秒,但是一旦延迟过去,你必须去!