3

I need to execute dynamically a function for all items of an array, but Array.forEach execute in sequence and I need execute in asynchronous.

items.forEach(function(item) {
    doSomething(item);
});

I try this:

var promises = [];

items.forEach(function(item) {
    var promise = function() {
        return Q.fcall(function() {
            doSomething(item);
        });
    };
    promises.push(promise());
});

Q.all(promises).then(function () {
    otherFunction(datacontext.mainList); //use datacontext.mainList filled.
});

But the execution is always in sequence and I need the execution in parallel.

The doSomething(item) method:

function doSomething(item) {
        var children = getChildren(item); //get data from local with manager.executeQueryLocally
        var total = getTotal(children); //simple calculations
        datacontext.mainList.push({
            name: item.firstName() + ' ' + item.lastName(),
            total: total
        });
    }

Please help me.

kuskunko
  • 330
  • 6
  • 17
  • Look up WebWorkers, javascript is single-threaded. – OneOfOne Feb 12 '14 at 14:47
  • you can put items.forEach in setTimeout. setTimeout(function () {(your code goes here)}, 1) – Damian Krawczyk Feb 12 '14 at 14:49
  • You're trying to use the wrong tool for the job. Promises are not a tool to convert synchronous code into asynchronous code. They are a means of taming the complexity of asynchronous callback chains. – Scott Sauyet Feb 12 '14 at 14:52
  • This isnt code review but this is a good place for map `var promises = items.map(function() {/*create promise*/ return promise;});` – megawac Feb 12 '14 at 15:27
  • 1
    Can you clarify what you mean by “asynchronous”? “asynchronous” implies that `doSomething` will not be finished in one event. If it *does* finish in one event, it will always be executed in “serial”, but not necessarily in order. If `doSomething` returns a promise, or has a callback, and won’t finish in a single event, that opens up the possibility of “parallel”, where multiple jobs will have events that are interleaved. Either way, I can provide tips. – Kris Kowal Feb 12 '14 at 17:29
  • The method `doSomething` is very lazy and I need to execute the method in "parallel". How to execute this method in parallel? – kuskunko Feb 12 '14 at 19:46

2 Answers2

21

This answer assumes that doSomething is itself an asynchronous operation. This means that it will have to yield to the event loop and wait for another event at least once in a while. If doSomething is synchronous, there is no benefit to composing it asynchronously.

Within the realm of composite asynchronous jobs, there are serial and parallel variations. The serial model causes job(n + 1) to begin only after job(n) finishes. The parallel model starts all jobs initially and finishes when all jobs are finished. In both of these regards, I can give you tips.

In parallel, you can use Array map and Q.all, assuming doSomething accepts a value from jobs and returns a promise:

return Q.all(jobs.map(doSomething))

To do jobs in serial order, use Array reduce.

return jobs.reduce(function (previous, job) {
    return previous.then(function () {
        return doSomething(job);
    });
}, Q());

If you want to perform jobs in serial, but only proceed to the next job depending on the result of the first, you can use reduceRight to compose a function.

return jobs.reduceRight(function (next, job) {
    return function (previous) {
        return doSomething(job).then(function (result) {
            if (result.isGood(previous)) return result;
            return next(result);
        });
    });
}, function fallthrough(previous) {
    throw new Error("No result was satisfactory");
})();

If you have an array that contains functions that need to be executed in order, feeding the output of the previous into the input of the next, you can use reduce even more succinctly.

return functions.reduce(Q.when, Q());

The tutorial in Q’s readme covers more cases and I am told has been helpful https://github.com/kriskowal/q#tutorial

Kris Kowal
  • 3,866
  • 2
  • 24
  • 24
0

It appears as though you are executing the promise you are creating immediately during iteration as you push it to the array. Try changing...

promises.push(promise());

To...

promises.push(promise);
xspydr
  • 3,030
  • 3
  • 31
  • 49
  • I had tested with `promises.push(promise);` but this didn't work, and this code `promises.push(promise());` didn't execute the promise, the code `Q.all(promises)` execute all promises but in sequence. – kuskunko Feb 12 '14 at 14:55
  • 1
    Is your doSomething(item) method a synchronous method? Q will not turn your synch method into an asynch method. Your doSomething function needs to be asynch. – xspydr Feb 12 '14 at 14:56
  • Yes, It is. Please help me. How to transform doSomething(item) in asynchronous method? – kuskunko Feb 12 '14 at 15:02
  • I have edited my question and add `doSomething(item)` method. – kuskunko Feb 12 '14 at 15:15
  • 1
    You would need to either rewrite your doSomething method to make use of using callbacks. Or... You could use the async (https://github.com/caolan/async#asyncjs) library to convert your sync code to async. Which, in addition if there is not a need for promises in your code then you should get rid of Q... – xspydr Feb 12 '14 at 15:22
  • "But `async.js` is made for asynchronous tasks only". Review this question [How to execute functions in parallel with async.js?](http://stackoverflow.com/questions/21744724/how-to-execute-functions-in-parallel-with-async-js) – kuskunko Feb 13 '14 at 14:03
  • Check this question: [Parallel.js have problems with Blob in IE](http://stackoverflow.com/questions/21744104/parallel-js-have-problems-with-blob-in-ie) – kuskunko Feb 14 '14 at 14:48