2

How do I use Promises at the code below?

function async_f1(callback) {
    setTimeout(function() {
        callback("Async function 1...");
    }, 1000);
}

function async_f2(callback) {
    setTimeout(function() {
        callback("Async function 2!...");
    }, 1000);
}

function async_f3(callback) {
    setTimeout(function() {
        callback("Second async function 3!...");
    }, 1000);
}

function doAll() {
    async_f1(function(result1) {
        async_f2(function(result2) {
            async_f3(function(result3) {
                console.log("Final result:", result1 + " " + result2 + " " + result3);
            })
        })
    });

}

doAll();

Thank you

scaryguy
  • 7,720
  • 3
  • 36
  • 52
  • You don't have any async functions. Just replace `setTimeout()` with `Q.delay()`. – SLaks Mar 26 '15 at 20:10
  • Yeah you're right. I had tried to simulate an async function there, if I failed on that, could you please show me how to do it with proper async functions? – scaryguy Mar 26 '15 at 20:11
  • I you make your own async functions compatible with the node.js async calling style with the first argument to the callback an error code (0 = no error) and the subsequent arguments the results, then you can very easily use Q or Bluebird functions to automatically make an async version of any method. If you don't make your async functions compatible with the calling style, then it's a little messier. – jfriend00 Mar 26 '15 at 21:58

3 Answers3

0

See the documentation.

For example:

var promise = Q.nfcall(someAsyncFunction, arg1, ...);
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • The thing is I've read a lot about it but maybe my stupidity, I couldn't implement it. I already seen documentation. If you'd like to answer my question, please be kind enough to give a detailed, explanatory answer. Thank you. – scaryguy Mar 26 '15 at 20:13
  • What don't you understand? Without a specific example, there aren't any more details I can give you. See the docs. – SLaks Mar 26 '15 at 20:14
  • See also http://blog.slaks.net/2015-01-04/async-method-patterns/ and especially http://blog.slaks.net/2015-01-05/introducing-promises/ – SLaks Mar 26 '15 at 20:14
  • Thank you for links, I'm going to check it out, your blog posts look "promising" :) Nevertheless, I've upated my question, could you directly answer it? It will really help me to understand it practically. Thank you for your patience! – scaryguy Mar 26 '15 at 20:26
  • @scaryguy: `Q.nfcall(async_f1).then(function(result) { ... })` (or `Q.all(...)`) – SLaks Mar 26 '15 at 21:05
  • thank you for giving your time @SLaks but I answered my own question. – scaryguy Mar 26 '15 at 21:11
0

Here is working snippet with promises using Q.nfcall:

var Q = require('q');


var a = Q.nfcall(function async_f1(callback) {
    setTimeout(function() {
        callback(null, "Async function 1...");
    }, 1000);
});

var b = Q.nfcall(function async_f2(callback) {
    setTimeout(function() {
        callback(null, "Async function 2!...");
    }, 1000);
});

var c = Q.nfcall(function async_f3(callback) {
    setTimeout(function() {
        callback(null, "Second async function 3!...");
    }, 1000);
});

function doAll() {
    Q.all([a, b, c]).then(function(result) {
        console.log(result);
    }).fail(function(err) {
        console.log(err);
    })

}

doAll();

Here is Q.denodeify version:

var Q = require('q');


var a = function async_f1(callback) {
    setTimeout(function() {
        callback(null, "Async function 1...");
    }, 1000);
};

var b = function async_f2(callback) {
    setTimeout(function() {
        callback(null, "Async function 2!...");
    }, 1000);
};

var c = function async_f3(callback) {
    setTimeout(function() {
        callback(null, "Second async function 3!...");
    }, 1000);
};

var a_promise = Q.denodeify(a);
var b_promise = Q.denodeify(b);
var c_promise = Q.denodeify(c);

function doAll() {
    Q.all([a_promise(), b_promise(), c_promise()]).then(function(result) {
        console.log(result);
    }).fail(function(err) {
        console.log(err);
    })

}

doAll();

My previous attempts were failing because I missed an important point:

In order to Q.nfcall work, your callback must use callback(error, data) pattern. Previously I didn't add error argument so I didn't get any errors but neither any output. Once I fixed it as callback(null, "Async function 1") it started to work.

scaryguy
  • 7,720
  • 3
  • 36
  • 52
  • But your original code does call them in sequence (taking 3 instead of 1 seconds)?! – Bergi Mar 26 '15 at 21:33
  • I'm sorry Bergi but I didn't get the point? With promises it takes only 1 second because they run in parallel? – scaryguy Mar 26 '15 at 21:39
  • Yes, exactly. But I thought you wanted to replicate the sequential behaviour of the original script? – Bergi Mar 26 '15 at 21:44
  • No no, I didn't mean that. Snippet in my post was just an example for some nested/dependent async functions. – scaryguy Mar 26 '15 at 21:50
0

At first, you'll want to promisify your functions. For interfacing with nodeback functions you'd usually use Q.denodeify, but your example functions always pass their results back in the first argument. So we'll need to write our own function:

function promisify(fn) {
    return function() {
        var args = Array.prototype.slice.call(arguments), ctx = this;
        return Q.Promise(function(resolve) {
            fn.apply(ctx, args.concat([resolve]);
        });
    };
}

and use it like

var f1 = promisify(async_f1),
    f2 = promisify(async_f2),
    f3 = promisify(async_f3);

With these, you can write your doAll function using promise style:

function doAll() {
    return f1().then(function(result1) {
        return f2().then(function(result2) {
            return f3().then(function(result3) {
                return "Final result:", result1 + " " + result2 + " " + result3;
            });
        });
    }).then(function(res) {
        console.log(res);
    });
}
doAll();

If that callback pyramid does look odd to you, have a look at How do I access previous promise results in a .then() chain?.

But as your functions actually don't rely on results of the others, you could easily run them in parallel:

function doAll() {
    return Q.all([f1(), f2(), f3()]).then(function(result) {
        console.log("Final result:", result1 + " " + result2 + " " + result3);
    });
}
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you for your answer Bergi. Probably it's my bad but I'm really not sure why would anyone need to do something like that? If your assumption of need of writing our own promisify function would really exist, why would the code in my answer work? Am I missing something? – scaryguy Mar 26 '15 at 21:57
  • @scaryguy: The code in your answer works because the `async_f*` functions there are not the same as the ones in the question. Blame the OP :-) – Bergi Mar 26 '15 at 21:59
  • So yes, one would need to write an extra `promisify` function only if the callback-taking functions you have to work with don't comply to the node conventions. Luckily, that is rare, and you usually can use the given `Q.denodeify`. – Bergi Mar 26 '15 at 22:01