0

What is the best way to wait for the completion of all parallel async functions before returning data?

Request works asynchronously and the following function will return an empty array.

import request from 'request'

// List of urls
const urls = [
  'http://someurl.com/1.json',
  'http://someurl.com/2.json',
  'http://someurl.com/3.json',
]

function getData () {
  // An array that will contain data
  let result = []
  
  // Request data from all urls
  urls.forEach(i => {
    // Function works asynchronously
    request(i, (err, res, body) => {
      if(err) throw err
      
      const data = JSON.parse(body)
      
      result.push(i.someValue)
    })
  })

  return result // Returns an empty array :(
}
Entry Guy
  • 425
  • 1
  • 7
  • 18

4 Answers4

2

If you can use promises, the best way would be to use them. Make sure your request function returns a promise, so you can then something like:

var request = function request( url ) {
    return new Promise(function requestPromise( resolve, reject ) {
        myAjaxCallOrOtherAsyncCall( url, function( error, response ) {
            if (error) reject(error);
            else resolve(response);
        })
    });
};

var getData = function getData( urls ) {
    return Promise.all( urls.map(request) );
};

var urls = [
  'http://someurl.com/1.json',
  'http://someurl.com/2.json',
  'http://someurl.com/3.json',
];

getData(urls).then(function( responses ) {
    var results = responses.map(JSON.parse);
    // do somethign with async results
});
Shilly
  • 8,511
  • 1
  • 18
  • 24
0

Use Promise.all() to await all promises to complete, though this does require that you use a request library that returns promises, like axios.

import axios from 'axios'

// List of urls
const urls = [
    'http://someurl.com/1.json',
    'http://someurl.com/2.json',
    'http://someurl.com/3.json',
]

function getData() {
    return new Promise((resolve, reject) => {

        const promises = urls.map(url => axios.get(url)); // array of pending promises

        Promise.all(promises) // creates single promise that resolves when all `promises` resolve
            .then(responses => {
                const dataArray = responses.map(response => response.data);
                return resolve(dataArray);
            }) // resolves with an array of response data
            .catch(reject);
    })
}

getData()
    .then(data => {
        // do something
    })
Aron
  • 8,696
  • 6
  • 33
  • 59
0

Try to use the trigger. When all data is collected use

$(trigger_obj).trigger('loading_complete');

And then make it a handler

$(trigger_obj).on('loading_complete', function () {
  \\ logic ...
});
Mobetoh
  • 93
  • 1
  • 4
0

Using https://github.com/request/request-promise-native, you can facilitate Promise.all as others have already pointed out:

import Request from 'request-promise-native'

// List of urls
const urls = [
  'http://someurl.com/1.json',
  'http://someurl.com/2.json',
  'http://someurl.com/3.json',
]

// The simplest form to create a bunch of request promises:
Promise.all(urls.map(Request))

// Otherwise it could look as follows:
const urlPromises = urls.map(url => Request({
  uri: 'http://www.google.com'
  // and more options
}))

// Or with promise chains:
const urlPromiseChain = (url) => {
  return Request(url)
         .then(doSomethingWithResponse)
         .catch(logButDontFailOnError)
}

const urlPromiseChains = urls.map(urlPromiseChain)

Promise.all(urlPromises /* or urlPromiseChains */)
.then(responses => console.log(responses))
Alex Pánek
  • 413
  • 2
  • 8