2

I code JavaScript quite a bit and although I think I do understand workings of promises I'm not sure if I fully understand advantages that promises bring to JS world. Consider code below, simply asynchronous calls with callbacks containing furhter calls and so on.

(function doWorkOldSchool() {

    setTimeout(function() {

        // once done resolve promise

        console.log("work done");

        setTimeout(function goHome() {

            // once done resolve promise

            console.log("got home");

            try {

                setTimeout(function cookDinner() {

                    // this exception will not be caught

                    throw "No ingredients for dinner!";
                    console.log("dinner cooked");

                    setTimeout(function goToSleep() {

                        // once done resolve promise

                        console.log("go to sleep");

                    }, 2000);

                }, 2000);

            } catch (ex) {
                console.log(ex);
            }

        }, 2000);

    }, 2000);

}());

One problem I see with this:

  1. Exceptions thrown inside of callbacks are useless. Is it correct to say that as throw calls happen these throw calls are out of scope hence the exception cannot be called and bubbles all the way to the top? How this sort of exception can be dealt with?

  2. Second problem I see that this nesting business might get really deep and even though you can keep callback functions code outside of the setTimeout code, it might become a mess.

So first could someone please clarify if there is anything else that is obvious problem or advantage of this sort of coding?

Now, below I prepared program that does the same thing really, but this time using promises:

function doWork() {

    return new Promise(function(res, rej) {

        // do your asynchronous stuff

        setTimeout(function() {

            // once done resolve promise

            res("work done");

        }, 2000);

    });
}


function goHome(succ) {

    console.log(succ);

    return new Promise(function(res, rej) {

        // do your asynchronous stuff

        setTimeout(function() {

            // once done resolve promise

            res("got home");

        }, 2000);

    });
}


function cookDinner(succ) {

    console.log(succ);

    //if exception thrown here it will be caught by chained err handler
    throw "No ingredients for dinner Exception!";

    return new Promise(function(res, rej) {

        // do your asynchronous stuff

        setTimeout(function() {

            // something went wrong so instead of using throw we reject the promise

            rej("No ingredients for dinner!");

            // once done resolve promise

        }, 2000);

    });
}


function goToSleep(succ) {

    console.log(succ);

    return new Promise(function(res, rej) {

        // do your asynchronous stuff

        setTimeout(function() {

            // once done resolve promise

            res("zzz... zz..");

        }, 2000);

    });
}


doWork()
    .then(goHome)
    .then(cookDinner)
    .then(goToSleep)
    .then(function(succ) {

        console.log(succ);

    }, function(err) {
        console.log(err);
    });

Comparing to previous solution I see no obvious problems with this approach, apart from you obviously must have understanding of promises to code/maintain this thing. Advantages would be however:

  1. the exception thrown INSIDE OF handlers will be caught in err handler that is chained somewhere further.

  2. the rejected Promises will be caught by chained err handler

  3. the code is much cleaner

Now, is my understanding correct or are there any other advantages/disadvantages of each approach?

spirytus
  • 10,726
  • 14
  • 61
  • 75
  • Yes. Yes. Yes. I wouldn't worry too much about subsequent maintenance... Promises are part of ES6 draft and well documented. The cost of extra verbosity is more than offset by the readability of your top-level code. – spender May 21 '14 at 01:03

2 Answers2

0

Your code is employing some anti patterns, you should never create promises (e.g. by new Promise, "deferreds" and so on) in application code. You must also never mix callbacks with promises because then you lose exception bubbling (the point of promises) and make your code super verbose.

You can use a library or implement a delay yourself:

function delay(ms, val) {
     return new Promise(function(res){setTimeout(res.bind(null, val), ms);});
}

Then:

function doWork() {
    return delay(2000, "work done");
}

doWork()
    .then(function(succ) {
        console.log(succ);
        return delay(2000, "got home");
    })
    .then(function(succ) {
        console.log(succ);
        // Never throw strings, yet another anti-pattern
        throw new Error("No ingredients for dinner Exception!");
        // Pointless to add code after throw
    })
    .then(function(succ) {
        return delay(2000, "zzz.. zz..");
    })
    // Don't use the second argument of .then, use .catch
    .catch(function(err) {
        console.log("error: ", err);
    });

Why should you use .catch instead of the second argument? Well, compare to how you would write it synchronously:

try {
    doWork();
    console.log("work done");
    sleep(2000);
    console.log("got home");
    sleep(2000);
    throw new Error("No ingredients for dinner Exception!";)
    sleep(2000);
    console.log("zzz.. zz..");
}
catch(e) {
    console.log("error: ", err);
}

Get it?

Esailija
  • 138,174
  • 23
  • 272
  • 326
  • I'm not sure if I understand your first paragraph, could youplease elaborate or point to URL where this is explained? – spirytus May 25 '14 at 06:35
  • @spirytus if you look at my code and compare it to your code, doesn't that explain it? :P – Esailija May 25 '14 at 08:49
-1

Promises can only return/decide on a value once, so once a value is chosen it cannot be changed. So if a user clicks on a div once the Promise only executes once
Ex:

p = new Promise(function (res, rej) { 
var b = document.getElementById('helloWorld'); 
  b.onclick = function() {
    res(prompt('value'));
  }; 
});
p.then(function(val) { console.log(val); });

There will always be only one value that will be in the log. This would be useful for GUI and controls for games/applications. Also you could have two event listeners inside of the Promise, this is useful for loading images and files. Promises react even after you have added a succeed or failure handler, so you create a function for a success though the failure function is created later which normal event handlers would generate errors if it failed, though Promises don't and call the function later when it is created. This allows you to focus more on how to react to an event and not worry about the timing of things. Very useful for loading things.
Amazing Page On Promises

tzengia
  • 352
  • 5
  • 12