向一个API发出多个请求,该API每分钟只能处理20个请求
Make several requests to an API that can only handle 20 request a minute
我有一个方法返回一个promise,该方法在内部调用API,该API每分钟只能有20个请求。问题是我有一个大的对象数组(大约300个),我想为每个对象调用API。
目前我有以下代码:
const bigArray = [.....];
Promise.all(bigArray.map(apiFetch)).then((data) => {
...
});
但它无法处理时间限制。我希望我可以使用lodash
中的_.chunk和_.debounce之类的东西,但我无法集中注意力。有人能帮我吗?
如果你可以使用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
完全相同的顺序提供结果,因此您可以确定哪个结果与哪个请求相匹配。
当然,您可以使用计数器用通用promise自己编写代码,但由于它已经构建在Bluebird库中,我建议您这样做。
Async库也有类似的并发控制,尽管它显然不是基于promise的。
这是一个只使用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
});
在此处进行演示:http://jsfiddle.net/jfriend00/v98735uu/
您可以每分钟发送一个20个请求的块,或者每3秒间隔一个请求(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秒呼叫1:
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
将多个调用分组为一个调用。请参阅本文链接自文档
Debounce:把它想象成"将多个事件分组为一";。想象一下,你回家,进入电梯,门都关上了。。。突然你的邻居出现在大厅里,试图跳上电梯。要有礼貌!为他开门:你要离开电梯了。考虑到同样的情况可能会再次发生在第三个人身上,等等……可能会推迟几分钟离开。
节流:把它看作一个阀门,它调节执行的流量。我们可以确定函数在特定时间内被调用的最大次数。所以在电梯的比喻中。。你很有礼貌地让人们进来10秒,但一旦延迟过去,你就必须离开!
- 跨节点服务器上的多个 API 请求维护数据
- 在 Google 云端硬盘 API 请求中使用参数
- Angularjs的异步API请求
- 如何将头添加到ActiveResource以创建API请求
- Flux+Rreact.js-缓存API请求响应
- JavaScript中的Google Drive API请求
- API请求在使用phonegap调试时不工作,但在模拟器中工作,为什么
- 使用Jquery将JSON API请求的元素传递给数组
- 如何将访问令牌用于Facebook Opengraph javascript SDK api请求
- 从多个API请求推送到全局数组
- 确定 API 请求的来源
- 如何将 API 请求中的 JSON 获取到页面的 javascript 中
- 跟踪 Google 可视化 API 请求中的事件
- 为什么此 Google 日历 API 请求不返回开始时间等事件数据
- 取消以前由 ng-change 创建的 api 请求
- 来自 HTML/js 项目中的 .dll / .aspx 文件的 API 请求
- GiantBomb API 请求获取 HTML 而不是 JSON,Nodejs 请求模块
- 在 Angular JS 服务中调用 YouTube API 请求
- 如何限制 api 请求堆栈
- Javascript:具有多个查询参数的嵌套 JSON api 请求