0

I'm new into node.js and promise (Q), so please be kind. I want to chain nested promises with his executing parent chain and i can't find how to.

I've made a toy script to illustrate my pb, you can launch it with node.js :

var Q = require("q");

function init() {
    return {nbIn: 0, nbOut: 0, stop: false};
}

function fn1(ctx) {
    var deferred = Q.defer();

    console.log("fn1:" + JSON.stringify(ctx));
    setTimeout(function() {
        console.log("fn1: resolve");
        deferred.resolve(ctx);
    }, 1000)

    return deferred.promise;
}

function sub1(ctx) {
    var deferred = Q.defer();

    console.log("sub1:" + JSON.stringify(ctx));
    setTimeout(function() {
        ++ctx.nbIn;
        console.log("sub1: resolve");
        deferred.resolve(ctx);
    }, 1000);

    return deferred.promise;
}

function sub2(ctx) {
    var deferred = Q.defer();

    console.log("sub2:" + JSON.stringify(ctx));
    setTimeout(function() {
        ++ctx.nbOut;
        if(ctx.nbOut === 3) {
            console.log("sub2: resolve");
            ctx.stop = true;
            deferred.resolve(ctx);
        }
        else {
            console.log("sub2: promise");
            return sub1(ctx).then(sub2);
        }
    }, 1000);

    return deferred.promise;
}

function fn2(ctx) {
    console.log("fn2:" + JSON.stringify(ctx));
    return sub1(ctx).then(sub2);
}

function fn3(ctx) {
    console.log("fn3:" + JSON.stringify(ctx));
}

Q.fcall(init).then(fn1).then(fn2).then(fn3);

It display:

fn1:{"nbIn":0,"nbOut":0,"stop":false}
fn1: resolve
fn2:{"nbIn":0,"nbOut":0,"stop":false}
sub1:{"nbIn":0,"nbOut":0,"stop":false}
sub1: resolve
sub2:{"nbIn":1,"nbOut":0,"stop":false}
sub2: promise
sub1:{"nbIn":1,"nbOut":1,"stop":false}
sub1: resolve
sub2:{"nbIn":2,"nbOut":1,"stop":false}
sub2: promise
sub1:{"nbIn":2,"nbOut":2,"stop":false}
sub1: resolve
sub2:{"nbIn":3,"nbOut":2,"stop":false}
sub2: resolve

I would like to chain the last line sub2 with fn3.

Any help appreciated, Thanks.

Sté
  • 195
  • 11

1 Answers1

0
setTimeout(function() {
    …
    return sub1(ctx).then(sub2);
}, …)

Here you're trying to return from an asynchronous plain callback. You cannot do that, the result will be lost as setTimeout does not care about it.

You can only return from .then() callbacks. In your case, it would look like this:

function sub2(ctx) {
    var deferred = Q.defer();

    console.log("sub2:" + JSON.stringify(ctx));
    setTimeout(function() {
        deferred.resolve(ctx);
    }, 1000);

    return deferred.promise.then(function(ctx) {
        ++ctx.nbOut;
        if (ctx.nbOut === 3) {
            console.log("sub2: resolve");
            ctx.stop = true;
            return ctx;
        } else {
            console.log("sub2: promise");
            return sub1(ctx).then(sub2);
        }
    });
}

You also can see that you're always using Q.defer together with a setTimeout now. You should make a helper function for that which returns a promise, and use deferreds as seldom as possible.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • ok, i get it. `setTimeout` just used to simulate real asynchronous functions, like `db.connect` / `query` / `insert`. – Sté May 21 '14 at 20:33
  • Well, the argument holds for them as well: Wrap them as tight as possible in helper functions (or [create those programmatically](http://stackoverflow.com/q/22519784/1048572)) that only spit out promises and do no processing. Do all the data manipulation and flow logic with those promises. – Bergi May 21 '14 at 21:34