Javascript 在使用ES6';让我们保证。好吗?
我有一些代码在一个从数据库中查询出来的列表上进行迭代,并对该列表中的每个元素发出HTTP请求。这个列表有时可能是一个相当大的数字(以千为单位),我希望确保我没有用数千个并发HTTP请求访问web服务器 此代码的缩写版本当前看起来像这样Javascript 在使用ES6';让我们保证。好吗?,javascript,node.js,es6-promise,Javascript,Node.js,Es6 Promise,我有一些代码在一个从数据库中查询出来的列表上进行迭代,并对该列表中的每个元素发出HTTP请求。这个列表有时可能是一个相当大的数字(以千为单位),我希望确保我没有用数千个并发HTTP请求访问web服务器 此代码的缩写版本当前看起来像这样 function getCounts() { return users.map(user => { return new Promise(resolve => { remoteServer.getCount(user) // m
function getCounts() {
return users.map(user => {
return new Promise(resolve => {
remoteServer.getCount(user) // makes an HTTP request
.then(() => {
/* snip */
resolve();
});
});
});
}
Promise.all(getCounts()).then(() => { /* snip */});
此代码正在节点4.3.2上运行。重申一下,
Promise.all
是否可以进行管理,以便在任何给定时间只有一定数量的承诺在进行?请注意,Promise.all()
不会触发承诺开始工作,而创建承诺本身会触发承诺
考虑到这一点,一个解决方案是在承诺得到解决时检查是否应该开始新的承诺,或者您是否已经达到了极限
然而,这里真的没有必要重新发明轮子。从他们的例子来看:
// On the Web, leave out this line and use the script tag above instead.
var PromisePool = require('es6-promise-pool')
var promiseProducer = function () {
// Your code goes here.
// If there is work left to be done, return the next work item as a promise.
// Otherwise, return null to indicate that all promises have been created.
// Scroll down for an example.
}
// The number of promises to process simultaneously.
var concurrency = 3
// Create a pool.
var pool = new PromisePool(promiseProducer, concurrency)
// Start the pool.
var poolPromise = pool.start()
// Wait for the pool to settle.
poolPromise.then(function () {
console.log('All promises fulfilled')
}, function (error) {
console.log('Some promise rejected: ' + error.message)
})
不要使用承诺来限制http请求,而是使用节点的内置功能。这消除了使用库或编写自己的池代码的要求,并具有额外的优势,可以更好地控制所限制的内容 agent.maxSockets 默认设置为无穷大。确定代理可以为每个源打开多少并发套接字。来源是“主机:端口”或“主机:端口:本地地址”组合 例如:
var http = require('http');
var agent = new http.Agent({maxSockets: 5}); // 5 concurrent connections per origin
var request = http.request({..., agent: agent}, ...);
如果向同一来源发出多个请求,将keepAlive
设置为true也可能对您有所帮助(有关更多信息,请参阅上面的文档)。蓝鸟可以使用并发选项来控制并行运行的承诺数量。有时它比.all
更容易,因为您不需要创建promise数组
const Promise = require('bluebird')
function getCounts() {
return Promise.map(users, user => {
return new Promise(resolve => {
remoteServer.getCount(user) // makes an HTTP request
.then(() => {
/* snip */
resolve();
});
});
}, {concurrency: 10}); // <---- at most 10 http requests at a time
}
const Promise=require('bluebird'))
函数getCounts(){
return Promise.map(用户,用户=>{
返回新承诺(解决=>{
remoteServer.getCount(用户)//发出HTTP请求
.然后(()=>{
/*剪断*/
解决();
});
});
},{concurrency:10});//如果您知道迭代器是如何工作的,以及迭代器是如何使用的,您就不需要任何额外的库,因为您自己构建自己的并发非常容易。让我来演示一下:
/*[Symbol.iterator]()等同于.values()
常量迭代器=[1,2,3][Symbol.iterator]()*/
常量迭代器=[1,2,3]。值()
//使用for..of循环所有项目
for(迭代器的常数x){
console.log('x:',x)
//注意这个循环如何继续相同的迭代器
//并消耗迭代器的其余部分,使
//外部循环不再记录x
for(迭代器常数){
console.log('y:',y)
}
}
因此,我尝试使所示的一些示例适用于我的代码,但由于这仅适用于导入脚本,而不适用于生产代码,因此使用npm包对我来说无疑是最简单的方法
注意:需要运行时来支持承诺或多填充。
Api
batchPromises(int:batchSize,array:Collection,i=>Promise:Iteratee)
承诺:每个批处理后都将调用Iteratee
使用:
批处理承诺
轻松批量承诺
注意:需要运行时来支持Promise或要填充。
应用程序编程接口
batchPromises(int:batchSize,array:Collection,i=>Promise:Iteratee)
承诺:每个批处理后都将调用Iteratee。
使用:
从“批量承诺”导入批量承诺;
批承诺(2[1,2,3,4,5],i=>新承诺((解决,拒绝)=>{
//迭代对象将在每个批次后激发,导致以下行为:
//@100ms解析第1项和第2项(第一批第2项)
//@200ms解决第3项和第4项(第二批,共2项)
//@300ms解决剩余项目5(最后剩余批次)
设置超时(()=>{
决议(i);
}, 100);
}))
。然后(结果=>{
console.log(结果);//[1,2,3,4,5]
})这就是我在这里的代码中使用Promise.race所做的
const identifyTransactions = async function() {
let promises = []
let concurrency = 0
for (let tx of this.transactions) {
if (concurrency > 4)
await Promise.race(promises).then(r => { promises = []; concurrency = 0 })
promises.push(tx.identifyTransaction())
concurrency++
}
if (promises.length > 0)
await Promise.race(promises) //resolve the rest
}
如果你想看一个例子:p-Limit
我将promise并发限制与自定义脚本、bluebird、es6 promise池和p-limit进行了比较。我认为这是最简单、最精简的实现
要求
与示例中的async兼容
- 节点版本>
我的例子
在本例中,我们需要为数组中的每个URL运行一个函数(例如,可能是一个API请求)。这里称为fetchData()
。如果我们要处理数千个项目的数组,并发性对于节省CPU和内存资源肯定是有用的
const pLimit = require('p-limit');
// Example Concurrency of 3 promise at once
const limit = pLimit(3);
let urls = [
"http://www.exampleone.com/",
"http://www.exampletwo.com/",
"http://www.examplethree.com/",
"http://www.examplefour.com/",
]
// Create an array of our promises using map (fetchData() returns a promise)
let promises = urls.map(url => {
// wrap the function we are calling in the limit function we defined above
return limit(() => fetchData(url));
});
(async () => {
// Only three promises are run at once (as defined above)
const result = await Promise.all(promises);
console.log(result);
})();
控制台日志结果是已解析承诺响应数据的数组。下面是流式处理和“p-limit”的基本示例。它将http读取流流传输到mongo db
const stream = require('stream');
const util = require('util');
const pLimit = require('p-limit');
const es = require('event-stream');
const streamToMongoDB = require('stream-to-mongo-db').streamToMongoDB;
const pipeline = util.promisify(stream.pipeline)
const outputDBConfig = {
dbURL: 'yr-db-url',
collection: 'some-collection'
};
const limit = pLimit(3);
async yrAsyncStreamingFunction(readStream) => {
const mongoWriteStream = streamToMongoDB(outputDBConfig);
const mapperStream = es.map((data, done) => {
let someDataPromise = limit(() => yr_async_call_to_somewhere())
someDataPromise.then(
function handleResolve(someData) {
data.someData = someData;
done(null, data);
},
function handleError(error) {
done(error)
}
);
})
await pipeline(
readStream,
JSONStream.parse('*'),
mapperStream,
mongoWriteStream
);
}
如果不想使用外部库,递归就是答案
downloadAll(someArrayWithData){
var self = this;
var tracker = function(next){
return self.someExpensiveRequest(someArrayWithData[next])
.then(function(){
next++;//This updates the next in the tracker function parameter
if(next < someArrayWithData.length){//Did I finish processing all my data?
return tracker(next);//Go to the next promise
}
});
}
return tracker(0);
}
downloadAll(someArrayWithData){
var self=这个;
变量跟踪器=功能(下一步){
返回self.someExpensiveRequest(someArrayWithData[next])
.然后(函数(){
next++;//这将更新tracker函数参数中的next
如果(next
它可以使用递归来解决
其思想是,最初您发送允许的最大数量的请求,并且这些请求中的每一个都应该在完成时递归地继续发送自己
function batchFetch(urls, concurrentRequestsLimit) {
return new Promise(resolve => {
var documents = [];
var index = 0;
function recursiveFetch() {
if (index === urls.length) {
return;
}
fetch(urls[index++]).then(r => {
documents.push(r.text());
if (documents.length === urls.length) {
resolve(documents);
} else {
recursiveFetch();
}
});
}
for (var i = 0; i < concurrentRequestsLimit; i++) {
recursiveFetch();
}
});
}
var sources = [
'http://www.example_1.com/',
'http://www.example_2.com/',
'http://www.example_3.com/',
...
'http://www.example_100.com/'
];
batchFetch(sources, 5).then(documents => {
console.log(documents);
});
函数batchFetch(URL、concurrentRequestsLimit){
返回新承诺(解决=>{
var文件=[];
var指数=0;
函数recursiveFetch(){
if(index==url.length){
返回;
}
获取(URL[index++])。然后(
npm install tiny-async-pool
const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
await asyncPool(2, [1000, 5000, 3000, 2000], timeout);
// Call iterator (i = 1000)
// Call iterator (i = 5000)
// Pool limit of 2 reached, wait for the quicker one to complete...
// 1000 finishes
// Call iterator (i = 3000)
// Pool limit of 2 reached, wait for the quicker one to complete...
// 3000 finishes
// Call iterator (i = 2000)
// Itaration is complete, wait until running ones complete...
// 5000 finishes
// 2000 finishes
// Resolves, results are passed in given array order `[1000, 5000, 3000, 2000]`.
while (funcs.length) {
// 100 at at time
await Promise.all( funcs.splice(0, 100).map(f => f()) )
}
Promise.allWithLimit = async (taskList, limit = 5) => {
const iterator = taskList.entries();
let results = new Array(taskList.length);
let workerThreads = new Array(limit).fill(0).map(() =>
new Promise(async (resolve, reject) => {
try {
let entry = iterator.next();
while (!entry.done) {
let [index, promise] = entry.value;
try {
results[index] = await promise;
entry = iterator.next();
}
catch (err) {
results[index] = err;
}
}
// No more work to do
resolve(true);
}
catch (err) {
// This worker is dead
reject(err);
}
}));
await Promise.all(workerThreads);
return results;
};
import chunk from 'lodash.chunk';
const maxConcurrency = (max) => (dataArr, promiseFn) =>
chunk(dataArr, max).reduce(
async (agg, batch) => [
...(await agg),
...(await Promise.all(batch.map(promiseFn)))
],
[]
);
const randomFn = (data) =>
new Promise((res) => setTimeout(
() => res(data + 1),
Math.random() * 1000
));
const result = await maxConcurrency(5)(
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
randomFn
);
console.log('result+++', result);