Skip to content

Instantly share code, notes, and snippets.

@kieranajp
Last active February 9, 2016 14:36
Show Gist options
  • Save kieranajp/d7affe2a4c858ec01bd7 to your computer and use it in GitHub Desktop.
Save kieranajp/d7affe2a4c858ec01bd7 to your computer and use it in GitHub Desktop.

The traditional way of doing async in JS was with callbacks:

function doSomethingThatTakesAWhile(callback) {
    // blah blah blah doing some work that will take a few seconds

    // We'll simulate this taking a while with the timeout
    setTimeout(function () {
        callback('done');
    }, 5000)
}

// Then use the function with:
doSomethingThatTakesAWhile(function (val) {
    alert(val); // 'done'... eventually
});

This falls down badly when you need a lot of things done, each of which takes a while:

function doSomething(callback) {
    setTimeout(function () {
        callback();
    }, 5000)
}

function doSomethingElse(callback) {
    setTimeout(function () {
        callback();
    }, 3000);
}

doSomething(
    doSomethingElse(
        function () {
            alert('done');
            // this is nicknamed the pyramid of doom because of all the nesting you end up with
        }
    )
);

There are some major problems here:

  • Code is difficult to read / refactor
  • Tasks have to happen in sequence
  • Handling errors is difficult - what happens if one task fails? You need a ton of try/catch handlers.

To get round this we can use "promises". Promises are just functions that don't return immediately. Instead, they return an object of type promise and then later, they return a second time with their actual return value.

So basically, when you call the function, it says "I promise to run and I'll let you know what my result is in a sec"

Using jQuery's promises, you could do something like this:

function doSomething() {
    // Create a new Promise (jQuery calls them 'deferreds' but they're the same thing)
    var d = $.Deferred();
    return setTimeout(function () {
        if (errorOccurred) {
            return d.reject(); // Make the promise fail
        }

        return d.resolve('hello'); // return 'hello'
    }, 5000)

    return d.promise(); // This is the Promise object that's returned immediately
    // basically saying "I will get back to you with the real return value soon"
}

function doSomethingElse(callback) {
    var d = $.Deferred();
    return setTimeout(function () {
        if (errorOccurred) {
            return d.reject();
        }

        return d.resolve('world');
    }, 3000)

    return d.promise();
}

$.when(
    doSomething(),
    doSomethingElse() // run all these promise functions simultaneously
).then(function (something, somethingElse) {
    // here we have the return values passed as function args, if we need them
    alert(something + ' ' + somethingElse);  // 'hello world'... eventually
}).catch(function (error) {
    console.error(error); // if any error at all occurred, we can deal here
});

Not only is this much neater code than callback hell, but it will run in 5 seconds rather than 8 seconds (5000 + 3000ms) because doSomething() and doSomethingElse() are run in parallel. We can also catch any errors with our one error handler on the end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment