tracer tong
tracer tong

Reputation: 553

Working with groups of promises

I am having difficulty understanding how to work with groups of promises that need to resolve together.

In my situation I want to make a series of promise calls based on an array of ids, and selectively add them to an output array. If this were PHP I would do something like:

$output = []
foreach($input as $id) {

  $result1 = functioncall1($id);
  $result2 = functioncall2($result1);

  if ($result2 == 'some filter') {
    $output[] = $result1;
  }
}

This can't be done with an asynchronous call as the loop does not wait for the result to return. So I'm using promises with bluebird like this:

Promise.map(input, function(id) {
    promisifedFunction1(id)
      .then(function(result1) {
          //process result1 some more

          promisifedFunction2(result1)
            .then(function(result2) {
                //process result2 some more

                if (result2 == 'some filter') {
                  return result1;
                });

            });

      })
  .then(function(output) {});
}

I am clearly missing something here because my output only ever contains an array of undefined. However it is apparently mapping and reaching the final then when I expect it to and those undefined arrays are only produced when the filtering oks it. The undefined is produced even if the filter just returns a literal string.

I'm clearing missing something but I'm struggling to fill in the gaps.

Upvotes: 2

Views: 1435

Answers (4)

Nandeep Barochiya
Nandeep Barochiya

Reputation: 426

You can use Promise.all

var promise1 = new Promise(function (resolve, reject) {
    // promise1 code here
    resolve(true);
})
var promise2 = new Promise(function (resolve, reject) {
    // promise2 code here
    resolve(true);
})
var promise3 = new Promise(function (resolve, reject) {
    // promise3 code here
    resolve(true);
})

Here we have used 3 Promises and now if we have to execute code after resolving all Promises we can use Promise.all

Promise.all([promise1, promise2, promise3]).then(() => {
    // code
});

Upvotes: 1

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276596

You can do eta-reduction to make that code look a lot simpler:

Promise.map(input, promisifedFunction1)
       .map(promisifiedFunction2)
       .filter(v => v === 'someFilter')
       .then(outputFunction);

Upvotes: 1

Naoric
Naoric

Reputation: 355

Make sure that your promisified functions are OK, this should also work:

var elements = ["one", "two", "three"]
var container = document.querySelector('.results')

function promisifiedFunc1(arrayElement) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('input to the next func ' + arrayElement)
    }, 1000)
  })
}

function promisifiedFunc2(inputFromPreviousFunc) {
  return new Promise(function(resolve, reject) {
    container.innerHTML += "<br>" + inputFromPreviousFunc
    setTimeout(function() {
      resolve('some filter')
    }, 1000)
  })

}

Promise.map(elements, function(one) {
    return promisifiedFunc1(one).then(promisifiedFunc2);
  })
  .filter(function(res2) {
    return res2 === 'some filter';
  })
  .then(function(allResults) {
    console.log(allResults);
    container.innerHTML += "<br>All Done!";
  })
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.5.0/bluebird.min.js"></script>
<div class="results"></div>

Promise.map allows you to map any array into an array of promises and generate a new promise that will be resolved once all these promises are resolved. Within the mapping function you can chain the two promises.

Upvotes: 1

Jared Smith
Jared Smith

Reputation: 22050

I assumed this would already have an answer here on SO, but apparently it doesn't (or if it does a couple of quick searches didn't find it).

You want a structure like this:

let filteredResults = Promise.all(arr.map(x => promisifiedFn1(x)))
  .then(xs => xs.map(result1 => promisifiedFn2(result1)))
  .then(ys => ys.filter(someFilteringFn));

Promise.all can be thought of as turning a container inside-out: you give it an array of Promises (by mapping your promise-returning function over an array of inputs) and it gives you a Promise of an array of the results which you then treat like any other array.

So xs is an array of the results of the first function mapped over the inputs once it all resolves. ys is the second. I'm not sure what bluebird's Promise.map does but you could possibly combine the first two steps in my code with it.

Since your doing a full processing of each input before deciding whether or not to keep the result, there's no penalty to simply processing the inputs with the two async steps and then filtering the resulting array once everything resolves.

Upvotes: 1

Related Questions