`Promise.all` 和大量异步操作的性能考虑
Performance considerations with `Promise.all` and a large amount of asynchronous operations
将 Promise.all
与异步代码一起使用时(如果是同步代码,则无需担心),您可能会遇到严重的性能(如果不是其他类型的话)问题,当您想要发送考虑到异步操作的接收端(例如本地文件系统、HTTP 服务器、数据库等)不能很好地处理那么多请求,请输出一大堆(数十、数百、数千甚至数百万)请求并行请求。
对于这种情况,如果我们能够告诉 Promise.all
我们希望同时进行多少个承诺,那将是完美的。然而,既然 A+ 应该是精简的,添加这些花哨的功能肯定没有意义。
那么实现这一目标的更好方法是什么?
由于我无法找到一个预先存在的库来处理 promise 批处理,所以我自己编写了一个简单的原语。它是一个 class,它包装了一个要执行的 function
数组,并将其分成给定大小的批次。它将等待每个批次完成,然后再 运行 下一个。这是一个相当幼稚的实现。在某些网络场景中,可能需要一个成熟的节流机制。
代码:
/**
* Executes any number of functions, one batch at a time.
*
* @see http://jsfiddle.net/93z8L6sw/2
*/
var Promise_allBatched = (function() {
var Promise_allBatched = function(arr, batchSize) {
if (arr.length == 0) return Promise.resolve([]);
batchSize = batchSize || 10;
var results = [];
return _runBatch(arr, batchSize, results, 0)
.return(results); // return all results
};
function _runBatch(arr, batchSize, results, iFrom) {
// run next batch
var requests = [];
var iTo = Math.min(arr.length, iFrom + batchSize);
for (var i = iFrom; i < iTo; ++i) {
var fn = arr[i];
var request;
if (fn instanceof Function) {
request = fn();
}
else {
request = fn;
}
requests.push(request); // start promise
}
return Promise.all(requests) // run batch
.then(function(batchResults) {
results.push.apply(results, batchResults); // store all results in one array
console.log('Finished batch: ' + results.length + '/' + arr.length);
})
.then(function() {
if (iTo < arr.length) {
// keep recursing
return _runBatch(arr, batchSize, results, iTo);
}
});
}
return Promise_allBatched;
})();
嗯,首先 - 不可能给 Promise.all
一个并发参数,因为 promises 表示 已经开始 操作,所以你不能在它们之前排队或等待正在执行。
您想以有限并发执行的是承诺返回函数。幸运的是 - bluebird 附带此功能(从版本 2.x 开始)使用 Promise.map
:
Promise.map(largeArray, promiseReturningFunction, {concurrency: 16});
并发参数决定一次可以发生多少个操作——注意这不是一个全局值——但只针对这个链。例如:
Promise.map([1,2,3,4,5,6,7,8,9,10], function(i){
console.log("Shooting operation", i);
return Promise.delay(1000);
}, {concurrency: 2});
请注意,无法保证执行顺序。
let arry=[1,2,4,........etc];//large number of array
while (arry.length) {
//I splice the arry 0,10...so you can change the limit as your //wish(note your server should be handle your limit
Promise.all(arry.splice(0, 10).map(async (eachArryElement) => {
let res = await yourAsyncMethod(eachArryElement);
}))
}
function yourAsyncMethod(data){
return new Promise((resolve, reject) => {
//your logic
resolve('your output')
})
}
将 Promise.all
与异步代码一起使用时(如果是同步代码,则无需担心),您可能会遇到严重的性能(如果不是其他类型的话)问题,当您想要发送考虑到异步操作的接收端(例如本地文件系统、HTTP 服务器、数据库等)不能很好地处理那么多请求,请输出一大堆(数十、数百、数千甚至数百万)请求并行请求。
对于这种情况,如果我们能够告诉 Promise.all
我们希望同时进行多少个承诺,那将是完美的。然而,既然 A+ 应该是精简的,添加这些花哨的功能肯定没有意义。
那么实现这一目标的更好方法是什么?
由于我无法找到一个预先存在的库来处理 promise 批处理,所以我自己编写了一个简单的原语。它是一个 class,它包装了一个要执行的 function
数组,并将其分成给定大小的批次。它将等待每个批次完成,然后再 运行 下一个。这是一个相当幼稚的实现。在某些网络场景中,可能需要一个成熟的节流机制。
代码:
/**
* Executes any number of functions, one batch at a time.
*
* @see http://jsfiddle.net/93z8L6sw/2
*/
var Promise_allBatched = (function() {
var Promise_allBatched = function(arr, batchSize) {
if (arr.length == 0) return Promise.resolve([]);
batchSize = batchSize || 10;
var results = [];
return _runBatch(arr, batchSize, results, 0)
.return(results); // return all results
};
function _runBatch(arr, batchSize, results, iFrom) {
// run next batch
var requests = [];
var iTo = Math.min(arr.length, iFrom + batchSize);
for (var i = iFrom; i < iTo; ++i) {
var fn = arr[i];
var request;
if (fn instanceof Function) {
request = fn();
}
else {
request = fn;
}
requests.push(request); // start promise
}
return Promise.all(requests) // run batch
.then(function(batchResults) {
results.push.apply(results, batchResults); // store all results in one array
console.log('Finished batch: ' + results.length + '/' + arr.length);
})
.then(function() {
if (iTo < arr.length) {
// keep recursing
return _runBatch(arr, batchSize, results, iTo);
}
});
}
return Promise_allBatched;
})();
嗯,首先 - 不可能给 Promise.all
一个并发参数,因为 promises 表示 已经开始 操作,所以你不能在它们之前排队或等待正在执行。
您想以有限并发执行的是承诺返回函数。幸运的是 - bluebird 附带此功能(从版本 2.x 开始)使用 Promise.map
:
Promise.map(largeArray, promiseReturningFunction, {concurrency: 16});
并发参数决定一次可以发生多少个操作——注意这不是一个全局值——但只针对这个链。例如:
Promise.map([1,2,3,4,5,6,7,8,9,10], function(i){
console.log("Shooting operation", i);
return Promise.delay(1000);
}, {concurrency: 2});
请注意,无法保证执行顺序。
let arry=[1,2,4,........etc];//large number of array
while (arry.length) {
//I splice the arry 0,10...so you can change the limit as your //wish(note your server should be handle your limit
Promise.all(arry.splice(0, 10).map(async (eachArryElement) => {
let res = await yourAsyncMethod(eachArryElement);
}))
}
function yourAsyncMethod(data){
return new Promise((resolve, reject) => {
//your logic
resolve('your output')
})
}