0

Well, I have the code below:

forEach(elements, element =>{
     if(condition){
        doSomething(element)
    } else {
       doSomethingElse(element)
    }
})

I would like make this in an async way, meaning the code waits until the operation performed on that element is completed before passing on to the next element.

I would appreciate your help thanks!

ryanpcmcquen
  • 6,285
  • 3
  • 24
  • 37
fatah
  • 573
  • 3
  • 9
  • 16

5 Answers5

0

You can use async / await. Assuming your methods return a Promise, you can do this:

const doSomething = () => {
   return new Promise(resolve => { resolve('do something done!') });
}

const doSomethingElse = () => {
   return new Promise(resolve => { resolve('do something else done!') });
}

const arr = [ 1, 2, 3, 4 ];

arr.forEach(async (item, i) => {
   let result = null;
   
   if (i % 2 === 0) { // or any other condition
       result = await doSomething();
   } else {
       result = await doSomethingElse();
   }
   
   console.log(result);
});
Daniel Conde Marin
  • 7,588
  • 4
  • 35
  • 44
0

Because you should avoid resolving promises in a for loop, as I said in comment you should store your promises in an array and resolve all of them after your loop. This should looks like this :

const promises = [];
elements.forEach(element =>{
     promises.push(condition ? doSomething(element) : doSomethingElse(element));
});

Promise.all(promises).then(...).catch(...);

This is only correct if doSomething and doSomethingElse are async functions.

Striped
  • 2,544
  • 3
  • 25
  • 31
  • This is actually worked, there is one typo, it is `Promise.all` and not `Promises.all`, my concern with this is that Promises are not compatible with IE – fatah Feb 12 '18 at 19:33
  • Actually, you can use a third library or Babel for IE compatibility, see here https://stackoverflow.com/questions/36016327/how-to-make-promises-work-in-ie11 – Striped Feb 12 '18 at 19:36
0

.forEach() is already synchronous (meaning it acts on one element at a time), just use it as described here:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

Your syntax is a bit off, it should look something like this (.forEach() is called as a method on the array it is operating on):

elements.forEach(element => {
    if (condition) {
        doSomething(element)
    } else {
       doSomethingElse(element)
    }
})
ryanpcmcquen
  • 6,285
  • 3
  • 24
  • 37
  • In my case, it seems like `forEach()` doesn't know weather `doSomething()` or `doSomethingElse()` is done executing before passing off the next element – fatah Feb 12 '18 at 19:24
  • @fatah, you wanted _asynchronous_ code, not _synchronous_ code. I edited your question to reflect that. Glad you found an answer! – ryanpcmcquen Feb 12 '18 at 19:52
  • async code is non-blocking, whereas in this case, i wanna block the process while when an element is going through the loop and the function depends on the condition – fatah Feb 12 '18 at 20:01
0

None of the answers provided thus far actually execute in the one at a time way you're looking for.

Promises invoke their executor function upon construction. So, by nature, in order to use Promise.all, all of your async operations must already be running. Think of it as Promise.allAtOnce.

@Daniel's answer comes close, but Array#forEach executes synchronously, so it still runs all at once:

const wait = (time, message) => new Promise(resolve => {
                setTimeout(_=>{
                  console.log(message);
                  resolve();
                }, time);
              });

let promises = [];
[3000, 2000, 1000].forEach(
  (time, index) => promises.push(wait(time, ++index))
)

Promise.all(promises);

/* 
    Even though `3000` was the first item, 
    `1000` resolves first because all the `Promise`
    executors run at once.
    
    Output: 
      3
      2
      1
    
*/

The most succinct way to execute one at a time is with an async loop:

const wait = (time, message) => new Promise(resolve => {
                    setTimeout(_=>{
                      console.log(message);
                      resolve();
                    }, time);
                  });
    
const iterate = async _ => {
  let index = 0;
  for (let time of [3000, 2000, 1000]) try {
    await wait(time, ++index)
  } catch (e) {
    console.error(e);
  }
}

iterate();

/* 
      Even though `3000` takes 3X longer to resolve, 
      the `wait` of `3000` resolves first, 
      because it is first in the iteration.
      Output: 
        1
        2
        3
        
*/

If you want to avoid using async/await, just chain the promises with a loop. This will produce the same result:

const wait = (time, message) => new Promise(resolve => {
                    setTimeout(_=>{
                      console.log(message);
                      resolve();
                    }, time);
                  });

const iterate = _ => {
  // initiate with an already-resolved promise
  let lastPromise = Promise.resolve(); 
  let index = 0;
  for (let time of [3000, 2000, 1000]) {
  // chain each promise to the promise returned by the previous item.
    lastPromise = lastPromise
      .then(result => wait(time, ++index))
      .catch(console.error);
  }
  return lastPromise;
}

iterate();

/* 
    Even though `3000` takes 3X longer to resolve, 
    the `wait` of `3000` resolves first, 
    because it is first in the iteration.

    Output: 
      1
      2
      3

*/
mccallofthewild
  • 521
  • 2
  • 6
0

Here's my attempt, using recursive function:

var i = 0, n = elements.length;

function doNext() {

    // exit if all elements have been accounted for
    if (i >= n) {
        return;
    }
    
    var element = elements[i];
    i++;
    
    if (condition) {
        doSomething(element).then(doNext);
    } else {
        doSomethingElse(element).then(doNext);
    }
}
wonderwalrus
  • 151
  • 3