Javascript 在使用ES6';让我们保证。好吗?

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

我有一些代码在一个从数据库中查询出来的列表上进行迭代,并对该列表中的每个元素发出HTTP请求。这个列表有时可能是一个相当大的数字(以千为单位),我希望确保我没有用数千个并发HTTP请求访问web服务器

此代码的缩写版本当前看起来像这样

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);