Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running promises in small concurrent batches (no more than X at a time)

The Async library has functions like eachLimit which can be used to efficiently spread a big batch of jobs over multiple CPU cores, like this:

var numCPUs = require('os').cpus().length;
var exec = require('child_process').exec;

async.eachLimit(someArray, numCPUs, function (value, done) {
  exec('something --input' + value, done);
}, finalCallback);

This avoids overloading the system with too many commands at once, but still exploits multiple CPUs.

I want to do the same thing but with Promises.

In the Bluebird API, I can't see any obvious way to do this kind of batching in such a concise, expressive way as with Async.

Is there a good pattern for doing this with Bluebird (or with Promises generally)? Or any other utility library I could use for this?

like image 774
callum Avatar asked Jun 08 '15 11:06

callum


People also ask

Is there any limit for promise all?

Assuming we have the processing power and that our promises can run in parallel, there is a hard limit of just over 2 million promises.

Are promises concurrent or parallel?

In single-core CPU the promises would run concurrently and in multi-core CPU they can be executed (!) in parallel for CPU intensive tasks.

What is the difference between promise all and promise allSettled?

all() method returns an array as an output containing promise data inside several indexes. Promise. allSettled() method returns an array of objects and each of these objects further contains two properties further status and value.

Does promise all use multiple threads?

Often Promise. all() is thought of as running in parallel, but this isn't the case. Parallel means that you do many things at the same time on multiple threads. However, Javascript is single threaded with one call stack and one memory heap.


1 Answers

In Bluebird, you can use Promise.map function with the concurrency option, like this

require('bluebird').map([1, 2, 3, 4, 5, 6], function (currentNumber) {
    console.log(currentNumber);
    return Promise.delay(currentNumber * 2, 1000);
}, {concurrency: 2}).then(console.error.bind(console));

Now, you can see that it is processing two values at a time. The value being processed may be in a different order, but the items in the result array will be in the same order corresponding to the original array.

PS: I introduced a delay of 1 second with Promise.delay, so that we can observe what is being processed currently.


Demo

function start() {
  document.getElementById("result").innerHTML = "";

  Promise.map([1, 2, 3, 4, 5, 6], function(currentNumber) {
    document.getElementById("result").innerHTML += currentNumber + "<br />";
    return Promise.delay(currentNumber * 2, 1000);
  }, {
    concurrency: parseInt(document.getElementById("concurrency").value, 10)
  }).then(function(result) {
    document.getElementById("result").innerHTML += result + "<br />";
  });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/2.9.27/bluebird.min.js"></script>
Limit: <input id="concurrency" value="2" />
<input type="button" onclick="start()" value="Start" />
<pre id="result" />
like image 126
thefourtheye Avatar answered Sep 18 '22 14:09

thefourtheye