0

Could someone explain to me why returning promises in promises callbacks is considered antipattern? Apparently it breaks exception bubbling but obviously my understanding of promises is lacking as I cannot clearly picture how would that work. My antipattern code is below:

var a = new Promise(function (res, rej) {

    setTimeout(function () {
        res("timeout");
    }, 1000);

});

a.then(function (res) {
    console.log("a");
    console.log(res);

    return new Promise(function (res, rej) {
        console.log("b");
        setTimeout(function () {
            res("timeout2");
        }, 2000);
    }).then(function (res) {
        console.log("c");
        console.log(res);

    }).then(function (res) {
        console.log("d");
        console.log(res);

    }, function (res) {
        console.log("e");
        console.log(res);

    });

}).then(function (res) {
    console.log("l");
    console.log(res);
});

EDIT:

This question is related to one of my previous questions where I do something similar with promises and one of the answers says:

"You must also never mix callbacks with promises because then you lose exception bubbling (the point of promises) and make your code super verbose."

So now I'm really confused if it is antipattern and if so why.

Community
  • 1
  • 1
spirytus
  • 10,726
  • 14
  • 61
  • 75
  • 3
    It’s not an antipattern. – Ry- Jul 12 '14 at 00:19
  • 1
    Are you talking about the deferred anti pattern ? https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns#the-deferred-anti-pattern – Willem D'Haeseleer Jul 12 '14 at 00:28
  • @WillemD'haeseleer I think not.. deferred antipattern is when you return promise or resolve it even though you don't have to. Again my understanding might not be correct, please let me know if thats the case. – spirytus Jul 12 '14 at 00:41
  • Returning a promise from a `.then()` handler IS how you chain multiple async activities one after the other using promises. There is nothing anitpattern about that concept. Can you please identify the exact place where you saw this antipattern that you think you're using? It appears you're mistaken about what this antipattern is, so the only way to clear that up will be for you to be more specific about what you thought the antipattern actually was. – jfriend00 Jul 12 '14 at 01:57
  • @false it most certainly is. The room for error here is enormous, promisification has to be do e at the lowest level. I'll write an answer later. – Benjamin Gruenbaum Jul 12 '14 at 11:49
  • 1
    @BenjaminGruenbaum: What, using the `Promise` constructor? I thought the question was asking about returning a promise from a `then` callback. – Ry- Jul 12 '14 at 15:55
  • Oh, I thought it was about doing the return `new Promise(function(res) { setTimeout(...` everywhere which _is_ a big mistake. – Benjamin Gruenbaum Jul 12 '14 at 18:22

1 Answers1

4

"You must also never mix callbacks with promises because then you lose exception bubbling (the point of promises) and make your code super verbose."

What Petka means here, is that you should not use callback based methods in your user code together with Promises because this will make your code very verbose, as illustrated by your example.

It's verbose because you have to manually resolve or reject a new Promise based on the callback every time you call the function.

setTimeout Example:
You have to manually call the resolver when the timeout completes. Now setTimeout is a bit of a poor example as it does not have a possibility to reject it's operation.

return new Promise(function (resolve, reject) {
        console.log("b");
        setTimeout(function () {
            resolve("timeout2");
        }, 2000);
    })

Better example
Say you want to call fs.readFile like so:

return new Promise(function (resolve, reject) {
    fs.readFile('foo.txt', function (err, res) {
        if (err){
            reject(err);
            return;
        } else {
            resolve(res);
        }
    });
})

Here you have to ether resolve or reject because you are mixing promises with callbacks, this will make your code very brittle and confusing.

Solution:

Instead of creating new Promises all over the place whenever you need to call a method that only supports callbacks, wrap that callback based method once and use that everywhere.

var readFileAsync = new Promise(function (resolve, reject) {
    fs.readFile('foo.txt', function (err, res) {
        if (err){
            reject(err);
            return;
        } else {
            resolve(res);
        }
    });
});

Bonus:

You don't even need to do this wrapping your self, bluebird has got you covered. Checkout promisification right here:

https://github.com/petkaantonov/bluebird/blob/master/API.md#promisification

This whole explanation is sort of beside the fact that it is completely valid to return a Promise inside a promise's onFulfilled method. This is part of the Promise/A+ spec, see the The Promise Resolution Procedure -> 2.3.2 If x is a promise, adopt its state [[3.4](#notes)]:

I assume your confusion originates from the fact that Petka is telling you not to create and return new promises in your example. But this has nothing to do with the fact that you are returning Promises, but rather with the fact that you shouldn't be creating promises at that place, as stated here above.

Use a wrapped version, and then you are good to return/chain them wherever you want.

Willem D'Haeseleer
  • 19,661
  • 9
  • 66
  • 99
  • thi is very good answer and makes it very clear why not to use callbacks with promises, thanks. Could you explain why exception bubbling is lost though? – spirytus Jul 12 '14 at 04:48
  • It's not actually 'lost', it's just that you have to do it manually everywhere over automatically by using promises. – Willem D'Haeseleer Jul 16 '14 at 08:26