slidefizz
slidefizz

Reputation: 267

Asynchonous issue with loop in nodejs

I'm trying to iterate threw a list of item and do some actions on them by calling an API like this example :

  for (i = 0; i < arr.length; i++) {  
    if (arr[i].id == 42) {
      api.requestAction(arr[i].id, function(error, response){ });
    }
 }

Problem is the loop obviously ended before all the requests are done and the program exits. What should I do to manage it ? I saw the "Promise" method but don't really know how I can use it in this case or maybe there's an other solution.

Thank you by advance !

Upvotes: 0

Views: 237

Answers (4)

Lee Brindley
Lee Brindley

Reputation: 6472

You could use async.js. It's an asyncronous control flow library which provides control flows for things like sequential loops, looping in parralel, and many other common flow control mechanism, check it out.

See code below, the code assumes that you're variable 'arr' is defined somewhere in scope.

npm install async

     var async = require("async");




     //Loop through each item, waiting for your
     //asyncronous function to finish before continuing
     //to move onto the next item in the array
     //NOTE: This does not loop sequentially, if you want that function with asyncjs then user eachSeries
     async.each(arr, 

         //Item is the current item being iterated over,
         //callback is the callback you call to finish the current iteration, it accepts an error and result parameter callback(error, result);
         function (item, callback) {
              api.requestAction(item.id, function(error, response){ 

                   //Check for any errors...     
                   if (error) return callback(error);
                   callback(null);
              });
         },
         function (err, result) {
              //You've now finished the loop

              if (err) {
                 //Do something, you passed an error object to
                 //in one of the loop's iterations
              }

              //No errors, move on with your code..
         });

Upvotes: 1

Shaharyar
Shaharyar

Reputation: 12439

Install bluebird

npm install bluebird --save

Code

//require npm
var Promise = require("bluebird");

//code
//"promisify" converts traditional callback function into a Promise based function
var _requestAction = Promise.promisify(api.requestAction);

//loop over array
Promise.map(arr, function (value) {
    if (value.id == 42) {
        //async request
        return _requestAction(value.id).then(function (_result) {
            //success
            console.log(_result);
        }).catch(function (e) {
            //error
            console.error(e);
        });
    }
});

Upvotes: 1

Endless
Endless

Reputation: 37786

With node-fetch (a promisify http api) you can together with async/await halt the for loop until it's done but this requires node v6+ with --harmony-async-await flag added

const fetch = require('node-fetch')

async function foo() {
  for (let item of arr) {
    if (item.id == 42) {
      let res = await fetch(url)
      let body = await res.text()
      console.log(body)
    }
  }
  console.log('done (after request)')
}

now every time you add the async keyword in front of a function it will always return a promise that resolve/rejects when everything is done

foo().then(done, fail)

alternetive you can just wrap you api fn in a promise if you don't want to install node-fetch

await new Promise((rs, rj) => {
  api.requestAction(arr[i].id, function(error, response){
    error ? rj(error) : rs(response)
  })
}) 

Upvotes: 1

Syed Faizan
Syed Faizan

Reputation: 961

Use Bluebird Promises:

var Promise = require('bluebird');

Promise.map(arrayOfIds, function(item){
  return api.requestAction(item);
})
.then(function(response){
 // all the requests are resolved here
})

if u want sequential execution of the ids then use Promise.mapSeries (is slow as it waits for task to finish)

Upvotes: 0

Related Questions