3

I am trying to loop through an array of AsynWork to be done. And cant flood the system with async work done all at the time. so I am trying to do one by one with promises. My problem is that I need to go though an array of values so each async works on each value of the array. I managed to do it with this code, but it works for my specific case. Can't make it general. What would be the approach to make it reusable for other type of arrays? I have seen some solutions with array.reduce then promises but cant figure it out. Also have seen examples with Q but not using, if it can be done with simple javascript would be better.

My Code:

function doSomething(ObjIn1, ObjIn2) {
    return new Promise(function(resolve, reject) {
        console.log("doSomething: ObjIn1: " + ObjIn1 + "  ObjIn2: " + ObjIn2);
        setTimeout(function() {
            console.log("doSomething Done: ObjIn1: " + ObjIn1 + "  ObjIn2: " + ObjIn2);
            resolve(ObjIn1, ObjIn2);
        }, 500);
    })
}

function LoopPromises(Function2Loop, functionOptions, Counter, Max) {
    console.log("Counter: " + Counter);
    if (Counter < Max) {
        Function2Loop.apply(this, [functionOptions[0][Counter], functionOptions[1]]).then(function() {
            Counter++;
            LoopPromises(Function2Loop, functionOptions, Counter, Max);
        });
    }
}

LoopPromises(doSomething, [
    ["A1", "A2", "A3"], "ARG2TESTE"
], 0, 3)
A1rPun
  • 16,287
  • 7
  • 57
  • 90
Jose Neto
  • 329
  • 1
  • 2
  • 8

1 Answers1

6

You're overthinking this :) A function with arguments is the same as a function without arguments closing over a function with arguments so:

a(1,2,3,4);

Is the same as

(() => a(1,2,3,4))(); 

Except perhaps negligibly slower. I'm assuming you need to queue the work for an arbitrary amount of promises. If you need to do it for a fixed number - you can just then between them. Let's see how we can do this:

// runs fn on the array elements in sequence, but 
function sequence(fns) { // fns - functions returning promises
    return fns.reduce((prev, nextFn) => { // 'fold' the array
        return prev.then(nextFn); // after the previous is done, execute the next
    }, Promise.resolve()); // start with an empty promise
}

Make sure you understand reduce first. For convenience - let's see an example without it:

function sequence(fns) { // fns - functions returning promises
    var queue = Promise.resolve();
    fns.forEach(fn => queue = queue.then(fn));
    return queue;
}

We're iterating through our array of work (functions) and executing them one after the other where we execute the next after the promise the previous returned resolved.

Where the values wait for each other based on the promise resolving (via then). This would let you do:

sequence([
   () => new Promise(r => setTimeout(r, 500));
   () => console.log("I only run after the previous work completed");
]);
Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • Hi Much thanks for the insights. It is a very good approach. It is a bit hard to see the way how the functions get looped, but once you know it, it's good. I had to edit some of the code to work: sequence([ () => doSomething("ObjIn1","ObjIn2"), () => console.log("I only run after the previous work completed") ]); – Jose Neto Feb 23 '16 at 09:27
  • getting into some problems now to build the array of functions. I am doing a for loop through an array of images to be worked. but when i do something like SequenceOfEditImage.push( () => EditImage(imagesArray[i]); // I get into problems cause the i key does not exist on the sequence function. Any idea how to work this? – Jose Neto Feb 23 '16 at 10:41
  • I was doing soemthing like this: – Jose Neto Feb 23 '16 at 12:00
  • for (var i = 0; (i < ImagesArray.length) ; i++) { SequenceOfEditImage.push( () => EditImage(ImagesArray[i]) ); } – Jose Neto Feb 23 '16 at 12:00
  • but now I changed to array.reduce and then building the sequence array with the "populated" values and works – Jose Neto Feb 23 '16 at 12:01
  • That's the closure loop problem - `i` in your example is the same value for each image - and is imagesArray.length so they're all `undefined`. If you change that `var` to `let` it'll work (with the for loop). See http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – Benjamin Gruenbaum Feb 23 '16 at 12:22
  • Yep, that works. thanks for giving some light on this blurry javascript :-) – Jose Neto Feb 23 '16 at 13:31