Sign up ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I wrote this as a convenient way to batch together multiple, repetitive AJAX calls. I was working in angular at the time, so I use the $q service, but I'm sure it could be adapted for a more general promise framework. I find it so useful, I'm interested in polishing it for general consumption.

// A utility function that can run for-like loops with asynchronous processes.
// Use just like forEach, but you can launch something like an ajax call in the
// loop, as long as you return a promise. You can choose to run the calls in
// serial (one after the other) or in parallel (launching them all at once).
// Either way, you can attach a callback when they're all done, and an array of
// the promise resolve values of each step is available.
// For example, this would call doAsyncJob() for every item in myList, one
// after the other, and run doSomethingElse() once those were all done:

// $forEachAsync(myList, function (item) {
//     return doAsyncJob(item);
// }, 'serial').then(function (arrayOfJobResults) {
//     doSomethingElse();
// });
MyAngularApp.factory('$forEachAsync', function ($q) {
    'use strict';
    return function forEachAsync(arrayOrDict, f, serialOrParallel) {
        if (serialOrParallel === 'parallel') {
            // Iterate over the data, calling f immediately for each data
            // point. Collect all the resulting promises together for return
            // so further code can be executed when all the calls are done.
            return $q.all(forEach(arrayOrDict, f));
        }
        if (serialOrParallel === 'serial') {
            // Set up a deferred we control as a zeroth link in the chain,
            // which makes writing the loop easier.
            var serialDeferred = $q.defer(),
                serialPromise = serialDeferred.promise,
                returnValues = [];
            // Do NOT make all the calls immediately, instead embed each data
            // point and chain them in a series of `then`s.
            // Note: this makes the assumption that forEach iterates over both
            // arrays and objects by providing only two arguments.
            forEach(arrayOrDict, function (a, b) {
                serialPromise = serialPromise.then(function (value) {
                    returnValues.push(value);
                    return f(a, b);
                });
            });
            // Fire off the chain.
            serialDeferred.resolve();
            // Return the whole chain so further code can extend it, making
            // sure to return the resolve values of each iteration as a list.
            return serialPromise.then(function (value) {
                // Add the final resolve value, and slice off the first, which
                // is the "zeroth link" value and is always undefined.
                returnValues.push(value);
                return returnValues.slice(1);
            });
        }
        throw new Error(
            "Must be 'serial' or 'parallel', got " + serialOrParallel
        );
    };
});
share|improve this question
    
In the serial case, it should be possible to construct returnValues in its entirety in the forEach loop without post-processing it. – Roamer-1888 Jun 30 at 0:05

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.