I'm still chugging along on Kids In Touch. Right now, I'm dealing with sending out account verifications. In a simple LAMP stack, this would be pretty easy for me. With Node.js, it's significantly more complicated. Dealing with asynchronous code in JavaScript just kills me. I'm just not very comformtable with this pattern.

Of course, I want to avoid callback hell. So, I am using the amazing Q promise library. I chose Q because AngularJs implements it very closely. I might as well have my front-end code in sync with my back-end code.

My verification process needs a few steps:

  1. Collect all accounts that need verifications sent out
  2. Iterate each account in the collection
  • Send Email
  • Update DB
  1. All Done

I want those actions under "Iterate each .." to be completed in sequence. So, I go to the documentation and find this:

return foo(initialVal).then(bar).then(baz).then(qux);

and:

var funcs = [foo, bar, baz, qux];

var result = Q(initialVal);
funcs.forEach(function (f) {
    result = result.then(f);
});
return result;

and :

return funcs.reduce(function (soFar, f) {
    return soFar.then(f);
}, Q(initialVal));

and finally this gem:

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

Wow. Again as a relative novice with Node.js, these examples confound me. It's pretty difficult to figure out what exactly happens with each of these various methods for chaining or sequencing promises.

So, I've written a small sample through trial and error. This example simply passes an object around from one promise to the next. Each promise adds new properties to the object.

var Q = require("q");

var slowFunction1 = function( myObject ) {

    console.log("\nRunning slowFunction1");
    console.log(myObject);

    var deferred = Q.defer();

    setTimeout( function() {
        console.log("slowFunction1 is resolving!");
        myObject.slowFunction1Processed = true;
        deferred.resolve( myObject );
    }, 1500);

    return deferred.promise;
};

var slowFunction2 = function( myObject ) {

    console.log("\nRunning slowFunction2");
    console.log(myObject);

    var deferred = Q.defer();

    setTimeout( function() {
        console.log("slowFunction2 is resolving!")
        myObject.slowFunction2Processed = true;
        deferred.resolve( myObject );
    }, 1500);

    return deferred.promise;
};


var slowFunction3 = function( myObject ) {

    console.log("\nRunning slowFunction3");
    console.log(myObject);

    var deferred = Q.defer();

    setTimeout( function() {
        console.log("slowFunction3 is resolving!")
        myObject.slowFunction3Processed = true;
        deferred.resolve( myObject );
    }, 1500);

    return deferred.promise;
};

var processes = [ slowFunction1, slowFunction2, slowFunction3 ];

var initialValue = { "stuff" : "it"};

var getFinalResult = function() {
    return processes.reduce( function( nextProcess, f ) {

        return nextProcess.then(f);

    }, Q(initialValue));

};

getFinalResult().then(
    function( response) {
        console.log("\nFinished running !");
        console.log(initialValue);

        console.log("\nHere is the returned response");
        console.log(response);
    }
);

This code produces the following output :

Running slowFunction1
{ stuff: 'it' }
slowFunction1 is resolving!

Running slowFunction2
{ stuff: 'it', slowFunction1Processed: true }
slowFunction2 is resolving!

Running slowFunction3
{ stuff: 'it',
  slowFunction1Processed: true,
  slowFunction2Processed: true }
slowFunction3 is resolving!

Finished running !
{ stuff: 'it',
  slowFunction1Processed: true,
  slowFunction2Processed: true,
  slowFunction3Processed: true }

Here is the returned response
{ stuff: 'it',
  slowFunction1Processed: true,
  slowFunction2Processed: true,
  slowFunction3Processed: true }

I'm a bit confused on what I did with getFinalResult. I'm not sure if that's a very good way to deal with getting the result of all the chained promises. Perhaps there was a simpler way.

Buzz me on Twitter if you have a better idea. Also, here is a Gist with the code. https://gist.github.com/calendee/7683499