ram abigadol
ram abigadol

Reputation: 11

make for loop wait for function to stop in node.js

I am new to the world of node.js and Javascript and I have a loop that goes over an array of objects at a certain condition I need to call a function that does asnyc work and the loop to stop while the function isn't done

fucntion foo1(arr){
  for(var i=0 ; arr.length>i ; i++){
        if(i==8){//or any other condition
            doAsyncStuff(hits[i])            
        }
    }
}

function doAsyncStuff(item){
 parser.parseURL(someurl,function(error,result){
  item.someprop=result.someprop;
 })
}

the problem is no matter what I do, I can't seem to make the function wait it end's before I have the result and doesn't update the item I need it to update. I understand it's a common issue but none of the solution I found worked. any help would be welcome. Thanks!

Upvotes: 0

Views: 342

Answers (3)

Xotic750
Xotic750

Reputation: 23472

If I understand you correctly then you could use a Promise chain, serialised using reduce (a for loop would also work), something like this

function doAsyncStuff(item) {
  return new Promise(resolve => {
    const time = Math.ceil(Math.random() * 2000 + 1000);
    window.setTimeout(() => {
      item.someprop = time;
      resolve();
    }, time);
  });
}

function foo1(arr) {
  return arr.reduce(
    (promise, item, index) => index % 2 === 0 ? promise.then(() => doAsyncStuff(item)) : promise,
    Promise.resolve()
  );
}

const hits = new Array(9).fill().map(() => ({}));
foo1(hits).then(() => {
  console.log(hits);
});

There is also Promise.all, which you could probably use (though not sure how wonderful that would be, I'm not a frequent Promise user).

Update: Using Promise.all

function doAsyncStuff(item) {
  return new Promise(resolve => {
    const time = Math.ceil(Math.random() * 2000 + 1000);
    window.setTimeout(() => {
      item.someprop = time;
      resolve();
    }, time);
  });
}

function foo1(arr) {
  return Promise.all(
    arr.map((item, index) => index % 2 === 0 && doAsyncStuff(item))
  );
}

const hits = new Array(9).fill().map(() => ({}));
foo1(hits).then(() => {
  console.log(hits);
});

I still haven't figured out the best way to format ES6, it always seems to end up with longish lines. (personal styling issue) :)

Upvotes: 0

Mopparthy Ravindranath
Mopparthy Ravindranath

Reputation: 3308

I would use Bluebird Promises and the reducer pattern.

var Promise = require('bluebird');

// iterates over the array 'arr' and calls fn
// Myfn repeatedly, optionally passing an initial.
// for the 1st iteration.  For subsequent iterations,
// the value returned by prev invocation of Myfn is passed.    
Promise.reduce(arr, Myfn, someInitialValue);

function Myfn(prev, item) {
   //return a value or a promise.
}

see documentation of Reduce here: http://bluebirdjs.com/docs/api/promise.reduce.html

Upvotes: 0

Mouad Debbar
Mouad Debbar

Reputation: 3226

Looping and doing async stuff is a little tricky in JS. You could use one of the libraries that @smnbbrv mentioned in his comment. But you could also do it yourself, which can help you understand how some of these libraries work.

function foo1(arr) {
  next(arr, 0)
}

function doAsyncStuff(item, cb) {
  parser.parseURL(someurl, function(error, result) {
    item.someprop = result.someprop;
    cb(result);
  })
}

function next(arr, i) {
  // Stop when we reach the end of the array.
  if (i >= arr.length) {
    return;
  }

  if (i == 8) { // or any condition
    // Move to the next item only when the async work is done.
    doAsyncStuff(arr[i], function() {
      next(arr, i + 1)
    })
  } else {
    next(arr, i + 1)
  }
}

Upvotes: 1

Related Questions