Emilio Grisolía
Emilio Grisolía

Reputation: 1203

Is this really an async "for-like" loop?

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?

Upvotes: 1

Views: 101

Answers (1)

Bergi
Bergi

Reputation: 664206

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! :-)

Upvotes: 1

Related Questions