0

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 !

7
  • Are you using any Promise library? Commented Dec 16, 2016 at 10:47
  • No I'm not using it because I don't really know how to process with it. Commented Dec 16, 2016 at 10:49
  • @shaharyar from OP: I saw the "Promise" method but not really know how I can use it in this case or maybe there's an other solution Commented Dec 16, 2016 at 10:50
  • Will you be OK if I answer with Promise? Because eventually you'll need to use it. You can't survive on callbacks totally. Commented Dec 16, 2016 at 10:50
  • jsbin.com/cokewocotu/edit?js,console this example uses promises and a loop to do some stuff. Commented Dec 16, 2016 at 10:51

4 Answers 4

1

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)
  })
}) 
Sign up to request clarification or add additional context in comments.

Comments

1

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);
        });
    }
});

2 Comments

var _requestAction = Promise.promisify(api.requestAction); here api.requestAction is a function call right.
No, it is to convert it into Promise. I've added explanation.
1

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..
         });

2 Comments

why do you wrap it in a closure?
Force of habit, just removed the encasing closure :) I do generally wrap any snippets in closure so I can copy and paste assured the code won't break anything else in the destination script. Good call for the readability of the answer though!
0

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)

1 Comment

ya your solution makes sense, i didn't see he was stopping the loop, thanks for mentioning

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.