Search code examples
javascriptnode.jsecmascript-6promisees6-promise

Loop with native promises;


I'm trying to make an asynchronous loop with native ES6 promises It kind of works, but incorrectly. I suppose I made a huge mistake somewhere and I need someone to tell me where it is and how it's done correctly

var i = 0;

//creates sample resolver
function payloadGenerator(){
    return function(resolve) {
        setTimeout(function(){
            i++;
            resolve();
        }, 300)
    }
}

// creates resolver that fulfills the promise if condition is false, otherwise rejects the promise.
// Used only for routing purpose
function controller(condition){
    return function(resolve, reject) {
        console.log('i =', i);
        condition ? reject('fin') : resolve();
    }
}

// creates resolver that ties payload and controller together
// When controller rejects its promise, main fulfills its thus exiting the loop
function main(){
    return function(resolve, reject) {
        return new Promise(payloadGenerator())
            .then(function(){
                return new Promise(controller(i>6))
            })
            .then(main(),function (err) {
                console.log(err);
                resolve(err)
            })
            .catch(function (err) {
                console.log(err , 'caught');
                resolve(err)
            })
    }
}


new Promise(main())
    .catch(function(err){
        console.log('caught', err);
    })
    .then(function(){
        console.log('exit');
        process.exit()
    });

Now the output:

/usr/local/bin/iojs test.js
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
fin
error: [TypeError: undefined is not a function]
error: [TypeError: undefined is not a function]
error: [TypeError: undefined is not a function]
error: [TypeError: undefined is not a function]
error: [TypeError: undefined is not a function]
error: [TypeError: undefined is not a function]
error: [TypeError: undefined is not a function]
caught [TypeError: undefined is not a function]
exit

Process finished with exit code 0

The good part: it reaches the end.

The bad part: it catches some errors and I don't know why.


Solution

  • Any helper function with promise looping I have seen actually made it much worse than what you can do out of the box with recursion.

    It is a little nicer with .thenReturn but yeah:

    function readFile(index) {
        return new Promise(function(resolve) {
            setTimeout(function() {
                console.log("Read file number " + (index +1));
                resolve();
            }, 500);
        });
    }
    
    // The loop initialization
    Promise.resolve(0).then(function loop(i) {
        // The loop check
        if (i < len) {              // The post iteration increment
            return readFile(i).thenReturn(i + 1).then(loop);
        }
    }).then(function() {
        console.log("done");
    }).catch(function(e) {
        console.log("error", e);
    });
    

    See it in jsfiddle http://jsfiddle.net/fd1wc1ra/

    This is pretty much exactly equivalent to:

    try {
        for (var i = 0; i < len; ++i) {
            readFile(i);
        }
        console.log("done");
    } catch (e) {
        console.log("error", e);
    }
    

    If you wanted to do nested loops it is exactly the same:

    http://jsfiddle.net/fd1wc1ra/1/

    function printItem(item) {
        return new Promise(function(resolve) {
            setTimeout(function() {
                console.log("Item " + item);
                resolve();
            }, 500);
        });
    }
    
    var mdArray = [[1,2], [3,4], [5,6]];
    Promise.resolve(0).then(function loop(i) {
        if (i < mdArray.length) {
            var array = mdArray[i];
            return Promise.resolve(0).then(function innerLoop(j) {
                if (j < array.length) {
                    var item = array[j];
                    return printItem(item).thenReturn(j + 1).then(innerLoop);
                }
            }).thenReturn(i + 1).then(loop);
        }
    }).then(function() {
        console.log("done");
    }).catch(function(e) {
        console.log("error", e);
    });