1

I was trying to understand a little bit more about promises and async programming. I was trying to make an async for loop (yeah, I know there are lots of libs to accomplish this, but they don't teach me how things work) using promises.

Let's say I want to iterate an array, and apply a function to one element "per tick" while I do some other stuff. So I made this "async for-loop-like" function:

function asyncFor_(elements, worker, index) {
    return new Promise((resolve, reject) => {
         process.nextTick(()=>{
             if (index < elements.length) {
                 try {
                    worker(elements[index])  
                    resolve(asyncFor_(elements, worker, index+1))   
                 } catch(e) {
                     reject()
                 }
             } else {
                 resolve()             
             }
         })
    })
}

And test it with this:

function logEnd() {
    console.log('End')
}
function logErr(e) {
    console.log(e) //Received
    console.log('I dont like 3. GTFO.')
}
function logItem(item) {
    if (item === 3) {
        throw Error('3? GTFO.')
    }
    console.log(item)
}
console.log('Begin')
asyncFor_([1,2,3,4,5], logItem, 0)
    .then(logEnd)
    .catch(logErr)
asyncFor_([6,7,8,9], logItem, 0)
    .then(logEnd)
    .catch(logErr)
 console.log('Displayed after begin and before the rest')

The output is:

Begin
1
6
2
7
8
I don't like 3. GTFO
9
End (End from the second asyncFor_ call)

I think this is working fine. But at the same time I'm having doubts. Maybe I am misinterpreting the results. Did I miss something? Is this "asynchrony" an illusion or is it really async?

JSON C11
  • 11,272
  • 7
  • 78
  • 65
Emilio Grisolía
  • 1,183
  • 1
  • 9
  • 16
  • 1
    This is working fine, but why do you need this to be done in a loop instead of making everything concurrent? – Explosion Pills Mar 23 '16 at 19:40
  • 1
    You should read http://stackoverflow.com/questions/16336367/what-is-the-difference-between-synchronous-and-asynchronous-programming-in-node – soyuka Mar 23 '16 at 19:41
  • @ExplosionPills I don't understand your question (maybe because i don't know what concurrency really is ahha I'm trying to make some kind of "request dispatcher". When a request arrives, a "dispatch" function looks for the adecuated handler (based on the URL an method) that are contained in an array (sounds horrible, i know). The thing is i don't want that the dispatch function "blocks" everything else while iterating the array :P just for useless code, don't worry haha – Emilio Grisolía Mar 23 '16 at 20:11
  • @soyuka I'll take a look :) – Emilio Grisolía Mar 23 '16 at 20:12
  • @EmilioGrisolía right now each element of the array does not get processed until the promise for the one before it is done. It seems like you could just process all of the items in the array at one time. – Explosion Pills Mar 23 '16 at 20:39
  • @ExplosionPills But if i process all the items at on time, each time they get processed, other operations will have to wait. Right? In this way, i think i'm accomplishing what i want: check one item of the first array, then check other item in another array, and then check te second item of the first and so on. Sorry, i don't have a good english level he he. – Emilio Grisolía Mar 23 '16 at 21:44

1 Answers1

1

Yes, it is fine, and yes, it is really asynchronous (as also evidenced by your output log from the two concurrent loops).

However it looks a bit like the Promise constructor antipattern, and by avoiding that you can greatly simplify your code:

function nextTick() {
    return new Promise(resolve => {
        process.nextTick(resolve);
    });
}

function asyncFor_(elements, worker, index) {
    return nextTick().then(() => {
        if (index < elements.length) {
            worker(elements[index]);
            return asyncFor_(elements, worker, index+1);
        }
    });
}

Putting your code in then callbacks, you've got the try-catch for free. Always promisify at the lowest possible level! :-)

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Hey man, thanks :) It is definitelly a more readeable version, although i really don't understand how it works. But i think i will find the answers by playing with your code :) I really appreciate your help, guys :) – Emilio Grisolía Mar 23 '16 at 21:53
  • 1
    You know about the [magic of `then`](http://stackoverflow.com/a/22562045/1048572), right? It's a quite literal transcription of your code, only with an extra promise only for the `nextTick`. – Bergi Mar 23 '16 at 21:59
  • Hey again :) I feel really lost about Promises, i won't lie to you haha. Let's se if i understand it... (1) Every time asyncFor_ is called, it will return a new Promise (returned by nextTick). (2) The "resolve" function is called every time then method is called? (3) The "then" method recives an argument, which is a function that must be called when the previous work has been finished (and the previous work is always putting the resolve function in the queue to be executed on the next nick)? (continued in the other comment) – Emilio Grisolía Mar 24 '16 at 02:05
  • 1
    No, `.then(…)` itself does return a new promise for the result of the callback, and it is this promise that is returned - not the one that `nextTick()` has created. `resolve` is only called once the next tick has happened, to fulfill only the promise that `nextTick` returned. – Bergi Mar 24 '16 at 02:11
  • (4) In the "resolve" function is where the job of asyncFor_ has to done? (5) After that, asyncFor_ is called again, and the previous steps are repetead again until it reaches the end condition? (6) If any error ocurrs, the function for handling it, must be specified in the .catch, right? But, is the same if i pass a "reject" function and call it inside the "then"? Sorry my bad english and the lots of questions. I've have the previous conclusions because of this test i made: http://pastebin.com/7LNu8RFm Thanks, again, and again, and again. – Emilio Grisolía Mar 24 '16 at 02:13
  • Shit i think i will code a lot more, i don't wanna make you lose more time. You have already helped me a lot. I really appreciate your help :) – Emilio Grisolía Mar 24 '16 at 02:15
  • Ahhhhhh, then() returns a promise, and a promise is fullfilled when it returns a value. If any error is thrown, the function specified in catch is called...? – Emilio Grisolía Mar 24 '16 at 02:19
  • 1
    Yes, like that :-) If an error is thrown in the callback, the promise rejected instead of fulfilled, and handlers for that case (from `.catch` or a second argument to `then`) will be called. – Bergi Mar 24 '16 at 02:21