Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execute promises concurrently with a buffer pool size in Javascript

I have a function with a promises which must be executed n times with different params each time. I want to chain the promises in a way that the script is always working on 3-4 promises at the time.

I made it with promise.all, this executes 3 concurrently and when all the promises resolves it goes on with the next 3.

How to make it work that when one of 3 resolves it starts immediatly with another but always working on max 3 at the time?

for( var i = 0; i < tasks.length; i++){

    if( i > 0 && i%3 == 0 ){

      await Promise.all([
       doTaskFunction(tasks[i]),
        doTaskFunction(tasks[i-1]),
        doTaskFunction(tasks[i-2]),
      ]);
    }

  }
like image 396
Pjotr Raskolnikov Avatar asked Feb 09 '18 15:02

Pjotr Raskolnikov


People also ask

Do JavaScript promises run in parallel?

Promises cannot "be executed". They start their task when they are being created - they represent the results only - and you are executing everything in parallel even before passing them to Promise.

How are promises executed in JavaScript?

Just to review, a promise can be created with the constructor syntax, like this: let promise = new Promise(function(resolve, reject) { // Code to execute }); The constructor function takes a function as an argument. This function is called the executor function .

Can we chain promises in JavaScript?

Introduction to the JavaScript promise chaining Note that the setTimeout() function simulates an asynchronous operation. The callback passed to the then() method executes once the promise is resolved. In the callback, we show the result of the promise and return a new value multiplied by two ( result*2 ).


2 Answers

You can achieve this fairly easy using es6-promise-pool:

const tasks = [
    (param) => new Promise(function(resolve, reject) {
        setTimeout(resolve, 2000, 'foo');
    }),
    () => new Promise(function(resolve, reject) {
        setTimeout(resolve, 2000, 'foo');
    }),
    () => new Promise(function(resolve, reject) {
        setTimeout(resolve, 2000, 'foo');
    }),
    () => Promise.resolve(1),
    () => Promise.resolve(2),
    () => Promise.resolve(3)
 ];
 
 let count = 1;

 const promiseProducer = () => {
    while(tasks.length) {
       console.log('processing ' + count++);
       const task = tasks.shift();
       return task(); // optionally you could pass a parameter here
    }
    
    return null;
 }
 
 const pool = new PromisePool(promiseProducer, 3); // concurrent Promises set to 3
 const poolPromise = pool.start();

 poolPromise.then(() => { console.log('done!'); })
<script src="https://cdn.jsdelivr.net/npm/[email protected]/es6-promise-pool.min.js"></script>
like image 179
Daniel Conde Marin Avatar answered Sep 30 '22 06:09

Daniel Conde Marin


I am just leaving my naive with generators implementation here! :)

function* myIteratorFactory(arr) {
  for (let i = 0; i < arr.length; i++) {
    yield(arr[i])
  }
}


function delayPromise(text, ms) {
  return function() {
    return new Promise((resolve, reject) => {
      console.log('[%s] Promise with value %s just started', new Date().toISOString(), text)
      setTimeout(() => resolve(text), ms)
    }).then(() => console.log('[%s] Promise with value %s just ended', new Date().toISOString(), text))
  }
}

var promArr = [
  delayPromise('hi', 1500),
  delayPromise('alex', 2000),
  delayPromise('how', 1700),
  delayPromise('are', 1800),
  delayPromise('you', 1500),
]

var que = 0
var myIterator = myIteratorFactory(promArr)


function executor(r) {

  while (que < 3) {
    var next = myIterator.next()
    if (next.done) return;

    next.value()
      .then(() => {
        que--
        executor(r)
        if (que == 0) r()
      })
    que++
  }



}
executor(() => console.log('i am done for today!'))
like image 44
Alex Michailidis Avatar answered Sep 30 '22 04:09

Alex Michailidis