JavaScript · Technical

Promises

Should I read this?

If you’re a JavaScript developer who still uses callbacks (on either the client side or server side) then this post is for you. If you don’t know what Promises are, then you probably will find it useful.

Back in time, JavaScript was initially built to add interactivity to web pages and to be used on the client side. It has been designed to handle user interactions using events and event handlers. Also to make communications with the server. All of these stuff are Asynchronous operations. we can say:

JavaScript is an event-driven programming language.

which means, the flow of the program is determined by events such as user actions (mouse clicks, key presses) or messages from other programs/threads.

So?

So, JavaScript is designed over and encourages the usage of callbacks.

Here’s a simple illustration of how a callback way works. #Main script wants to execute #Function_B after #Function_A finish execution. It’s important to keep in mind that #Function_A has nothing to do with the logic of #Function_B.

callbacks-way

#Function_A is now the parent of #Function_B and responsible for calling it at the correct time.

But …

But things become really complicated when we need to implement multiple asynchronous operations which depend on each other. example:

function func5() {} // depends on the results of async4() and
function async4() {} // depends on the results of async3() and
function async3() {} // depends on async2()
function async2() {} // depends on async1()

 

Now, let’s implement this logic using classical callbacks way:

async1(function() {
// do some stuff
async2(function(inputsFromAsync1) {
// do stuff that depends on inputsFromAsync1
async3(function(inputsFromAsync2) {
// do stuff that depends on inputsFromAsync2
async4(function(inputsFromAsync3) {
// more stuff that depends on inputsFromAsync3
// finally execute func5() that should "in time"
// get executed after all previous logic
func5();
});
});
});
});

 

Isn’t it annoying to have nested code blocks like these? Is it readable?

Screen Shot 2016-01-26 at 1.32.49 AM

Promises are the way of implementing asynchronous logic without the need of passing callbacks as arguments.

Following is a simple illustration of how things work using Promises.

promises-way

  • #Main script calls #Function_A
  • #Function_A executes and returns a promise
  • #Main script holds that promise and wait for #Function_A to trigger a success/failure signal
  • #Main script calls #Function_B explicitly

Here’s how the skeleton of an async operation using promises should look like:

function asyncSayHiTo(name) {
// define a defer
// defer is the context of the ASYNC operation
var defer = q.defer();
// make an async operation
setTimeout(function() {
// this should run after the function already terminated
// and returned a promise
if (name !== undefined) {
var hiMsg = 'Hi ' + name + '!';
// now we're keeping our promises and telling whoever holds the promise
// "Here you go! I'm done with my ASYNC stuff, and this is my output"
// note that we don't care which function is listening to this promise,
// and to whom exactly we're sending this output ... encapsulation!
// we do what we're supposed to do, and nothing more.
// equivalent to successCallback() we call .resolve()
defer.resolve(hiMsg);
} else {
// something wrong, we gave a promise, and we need to keep it
// but we can't pretend that everything is okay
// equivalent to failureCallback() we call .reject()
defer.reject('Come on! you need to give a name!');
}
}, 2000);
// return a promise that we will give you a response after sometime
// note that the following code executes before the callback
// of setTimeout() executes
return defer.promise;
}
// call an async operation
asyncSayHiTo('Amr')
// .then() is just the beautiful part, makes code much more readable
// and self descriptive, .then() comes after async operation.
.then(function result(hiMsg) {
console.log(hiMsg); // logs: "Hi Amr!"
});
// invalid call to an async operation
// it's invalid because we should pass it a 'name' argument
asyncSayHiTo()
.then(function result(hiMsg) {
console.log(hiMsg);
}, function error(reason) {
console.log('Oh! Something went wrong: ' + reason);
});

 

This is beautiful because now each ASYNC operation is encapsulated and has nothing to do with any other operation.

Let’s have a look of how our very first example should look like:

async1()
.then(function(inputsFromAsync1) {
return async2();
})
.then(function(inputsFromAsync2) {
return async3();
})
.then(function(inputsFromAsync3) {
return async4();
})
.then(function(inputsFromAsync4) {
return func5();
});

Now code looks much cleaner, readable, self descriptive, modularized (for unit testing and documentation).

It’s important to note that the .then() result is implicitly a promise. so, you can chain your operations with multiple .then()s

That’s all?

No, there’s a bunch of other handy features that promises do for you as well

  1. If -for example- we want to execute some logic after multiple promises get resolved. then we can use .all()
    function asyncOperation() {
    // returns promise
    }
    var promises = [];
    for (var i=0; i<=10; i++) {
    var promise = asyncOperation();
    promises.push(promise);
    }
    // now we need to execute some logic after all promises get resolved
    // we can use .all() as following
    q
    .all(promises)
    .then(function() {
    console.log('all promises resolved!');
    });
    view raw promises-all.js hosted with ❤ by GitHub
  2. If we need to execute some logic after either the promise get resolved or rejected, then we can use .finally()
    function asyncOperation() {
    // returns promise
    }
    // show blocking loading modal
    asyncOperation()
    .then(function() {
    // all fine
    }, function() {
    // something went wrong
    })
    .finally(function() {
    // on either case, we want to hide blocker
    // and show the proper message
    });
  3. If we need to send notifications from inside the ASYNC operation to whoever has the promise, then we can use .notify()
    function uploadFile(data) {
    // we can send notification one or more time as following:
    defer.notify(percentageUploaded);
    // returns promise
    }
    uploadFile()
    .then(function() {
    // fine
    }, function() {
    // error
    }, function(percenatage) {
    // get updates before resolving or rejecting the promise
    console.log('Uploaded ' + percenatage + '% ...');
    });

 

I hope I’ve convinced you to start thinking about promises seriously. And if you already use promises I hope that you learned something new about it.

Please feel free to give your feedback and start discussions … I don’t bite!

 

 

3 thoughts on “Promises

Leave a comment