user3195616 user3195616 - 1 year ago 56
Javascript Question

Run next in "foreach" only once previous finished in javascript

It might be smth simple, but I just cannot find the way to do it.
I have an array, and I want to do a few actions for each array value BUT only once the previous is finished.
Lets say I have an array [1,2,3,4,5] and I want to add img tag with a picture with the name "picture"+i+".jpg" ,if picture file doesnt exist - use default picture, do few more actions with that img and, only once its all done, do the next "foreach".

I am looking into promises but still cannot figure it out.
Problem is that its not just one async action (ajax or get) but a few different actions.


Answer Source

Not sure if this is what you need. But I did have to resolve several cases of async dialog boxes, so this should work. Note that this requires either a browser that supports Promise or require you to have a Promise shim.

var items = [1,2,3,4,5];
var chainObj = {}; // Set this up if you need to communicate info to subsequent items. Or you need some form of "reduce" capability.
var promise = Promise.resolve(accumObj); // The initial promise.
for (var i = 0; i < items.length; i++){
    var currItem = items[i];
    promise = promise.then(function(accumObj){
        if (/*Need Async Stuffs*/){
            return new Promise(function(resolve, reject){
                /*perform some async stuff for the currItem. Once they're done, update accumObj and invoke the below function*/
        } else {
            /* Perform non-async stuffs for currItem, update accumObj as needed. */
            return accumObj; // Return the accumObj for the next step.
promise.then(/*Put call back to finish up anything else that's left*/);

The above will guarantee that each "item" will not start processing until the previous item is done, as long as you only call "resolve" after all the async operations are done (if needed, chain multiple promises together within each loop as needed).

In short, the above call creates a long chain of promises like the below pseudo code:

.then(/*Process items[0]*/)
.then(/*Process items[1]*/)
.then(/*Process items[2]*/)
.then(/*Process items[3]*/)
.then(/*Process items[4]*/)    
.then(/*Process for when everything is done*/)

Each process step either resolves immediately (the else part, aka non-async path), or resolves after some async operations (the /*Need Async stuffs*/ path). And subsequent process step will only start once the previous process step resolves. The accumObj allow you to accumulate results from each step and pass it to the next one.

The final promise.then in the code block is where you can perform any final actions once everything is processed.

CAVEAT: The above example code does NOT handle any error/exceptions.

EDIT#1: To handle comparatively complex operation, modify the promise = promise.then... block to this.

promise = promise.then(function(accumObj){
            // Start running you async stuffs
            var promises = [];
            promises.push(new Promise(function(resolve, reject){
               // Async function #1. Invoke resolve when the async function completes (simplest solution, pass "resolve" to whatever callback the 
               // The async fucntion uses.
            promises.push(new Promise(function(resolve, reject){ // Async function #2. });
            promises.push(new Promise(function(resolve, reject){ // Async function #3. });
            // ...
            promises.push(new Promise(function(resolve, reject){ // Async function #Last. });

            // Do synchronous function here.
            return Promise.All(promises).then(function(results){
                // Process the results from your async calls, udpate accumObj as needed.
                return accumObj;

What the above does is firing off ALL your async function for the current item before processing your synchronous functions (letting the async run in parallel). The return Promise.All(promises).then(function(results){return accumObj;}); returns another promise that performs the following.

  1. "Wait" (this happens in an async manner) until every single promise in promises are resolved.
  2. Invoke the function in the then callback with the array of resolved values from each promise (results[0] is the resolved value or promises[0], and so on).
  3. The function returns the accumObj, which becomes the resolved value of return Promise.All(promises).then(function(results){return accumObj;});.
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download