Click here to Skip to main content
15,891,912 members
Articles / Programming Languages / Javascript

Promises in JavaScript - Past, Present and the Future

Rate me:
Please Sign up or sign in to vote.
4.50/5 (2 votes)
26 Jun 2016CPOL8 min read 16.9K   3   1
An article about the history, current trends and the future of JavaScript Promises
When we are dealing with async code and callbacks in JavaScript, then promises come into the picture. In this article, you will learn about the history, current trends and future of promises.

Introduction

Today, we cannot imagine writing JavaScript code which is not asynchronous in nature for any decent web application. Whether it's the service calls, animations or even simple stuff like displaying time on a web page, we must utilize JavaScript's asynchronous elements. So what exactly is synchronous and asynchronous programming? The very simple difference is that synchronous code must wait for its previous action to complete before moving on to execute the next action.

Synchronous code usually runs in a single thread. Async code must execute in separate threads. When the async JavaScript code runs, the browser takes care of managing the threads for us in simple scenarios. We always need to write async code when we have to wait for result before moving on to execute any task; for example when we make a Get request on the server, then we have to wait until the results are returned back to us and we can show them on the page. This requirement to wait leads to the need of callbacks.

A callback is simply a piece of code which gets called after the result of any async request is retrieved. Now callbacks can be of two types: success and failure. When the result is successfully received, then a success callback should be executed. When there is any problem in executing the asynchronous code, then a failure callback is executed. Both success and failure callbacks could be same or different functions in JavaScript which depend on our preference.

When we are dealing with async code and callbacks, then promises come into the picture. A promise is something which we receive in the future and can have several states; the most common are Promise Fulfilled and Promise Rejected.

As per github/kriskowal:

Quote:

If a function cannot return a value or throw an exception without blocking, it can return a promise instead. A promise is an object that represents the return value or the thrown exception that the function may eventually provide. A promise can also be used as a proxy for a remote object to overcome latency.

You can read the Commonjs specification for promises over here:

The Past

XML Http Request

Before we had jQuery's promises, the usual way to assign callbacks to async service calls was by using XHR callbacks. An Xhr callback can be assigned to success and failed responses from the server using designated status codes.

Following is the basic implementation of xhr object. In the simplest of scenarios, we will just create a new xhr object and then we will have to send the request. Before sending the request, we can assign a callback based on the ready state and the status of our request.

JavaScript
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
    xmlhttp = new XMLHttpRequest();
}
else
{// code for IE6, IE5
    xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
}
xmlhttp.onreadystatechange = function ()
{
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
    {
        successCallback();
    }
}
xmlhttp.open('GET', url, true);
xmlhttp.send();

The ready state can have the following values:

  • 0 = Unsent
  • 1 = Opened
  • 2 = Headers Received
  • 3 = Loading
  • 4 = Completed

XHR status can have the following values:

  • 200 = Okay
  • 404 = Page Not Found

In the current version, we can assign event listeners to xhr events.

JavaScript
var xhr = new XMLHttpRequest();
xhr.addEventListener("progress", progress);
xhr.addEventListener("load", complete);
xhr.addEventListener("error", failed);
xhr.addEventListener("abort", cancelled);

The callbacks could be normal functions:

JavaScript
function completed(data) {
    document.getElementById('container').innerHTML = data.Text;
}

Callbacks in jQuery

jQuery's $.ajax() had success and error callbacks which have been widely used to manage latency problems with the server in returning the data. Its implementation is simple; two JavaScript properties to which we assign functions which later get called in case of successfully getting the result or if there is any error.

JavaScript
$.ajax({
    method: "POST",
    url: "some.php",
    data: { name: "John", location: "Boston" },
    error: function (xhr, error) {
        //handle the error
    },
    success: function (data) {
        //handle the returned data
    }
});

The Present

.then(fn)

Fast forward to today and we see .then() everywhere in the JS code. I can bet that you will see this implementation in every mid to large sized application utilizing a decent JS framework. As the name implies, .then() is used to execute a callback code after a response is received from an async code execution.

Following is the jQuery implementation of .then() function using a Deferred Object:

JavaScript
$.when($.ajax("test.aspx")).then(function (data, textStatus, jqXHR) {
    alert(jqXHR.status);
});

Chaining Callbacks

There can be numerous cases when based on the result returned from one async code block execution, we need to make another async request. In such cases, we must chain the callbacks one after the another. To implement the callback chaining, we need a special kind of object which can store the callback information and can then queue their execution.

JavaScript
$.when($.ajax("someeUrl.aspx"))
    .then(function (data, textStatus, jqXHR) {
        $.ajax("someOtherUrl.aspx");
    })
    .then(function (data, textStatus, jqXHR) {
        $.ajax("someOtherUrl.aspx");
    })
    .then(function (data, textStatus, jqXHR) {
        $.ajax("yetAnotherUrl.aspx");
    }
);

Deferred Object

This object can facilitate callback chaining and their queued execution. A deferred object exposes several functions which could be used to chain success and failure callbacks. Other features may include getting the current state of deferred object and rejecting and resolving with a specific context.

Promise Implementation in jQuery

jQuery returns deferred objects from several APIs which involve some kind of async code execution. For instance, $.ajax() and many animation functions which return deferred objects and we can chain further callbacks to those objects.

JavaScript
$("#container").css("color", "red").slideUp(2000).slideDown(2000);

Other JS Code Libraries

There are other code libraries out there available for us to use if we don't want to use the inherent promise code structure of any framework. These libraries work more or less in the same way, that is to chain callbacks one after another for success and failed responses. The most prominent one is $q.

AngularJS has implemented $q to handle async code execution and provides the $q dependency as an in-built service which we can use in our custom service factories.

The motive of $q or any other such deferred object library is to fix the horizontally growing callback chains and convert them into linear code statements.

JavaScript
function callback1 (data) {
    function callback2(data) {
        function callback3(data) {
            function callback4(data) {
                //and so on
            }
        }
    }
}

The above code could be converted into a linear chain using deferred objects to chain promises.

JavaScript
someFunctionReturningPromise()
    .then(callBack1)
    .then(callBack2)
    .then(callBack3)
    .then(callBack4);

The Future

When we talk about the future of Promises in JavaScript, then it would not be wrong to say that the future is already here. The only thing holding the latest promise implementations to come into practice is the widespread use by the developer community. That too is changing as more and more companies and teams are switching over to the ES6/ES7 coding standards.

ES6 Promises

ES6 has a standard specification for a Promise object which could be used to handle the future results of an operation. The syntax of Promise constructor is as follows:

JavaScript
new Promise( /* executor */ function(resolve, reject) { ... } );

In the above, we must provide an executor function. An executor function gets executed when we call the then() method of the Promise object. The resolve and reject arguments are functions which we pass into the executor to handle the future outcome of the task.

Following is a very simple example which shows how it could be used:

JavaScript
<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <div id="container"></div>
    <script>
        var myPromise = new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve('Hello');
            }, 5000);
        });
        myPromise.then(function (message) {
            document.getElementById('container').innerHTML = message;
        });

    </script>
</body>
</html>

The above code will print a message of our choice in the container <div> element after 5 seconds. This syntax is still not compatible with most browsers so you must use a polyfill for older versions if you decide to use it in your application.

async Functions

You might have already written methods with the async keyword in C# language. Methods/Functions with the async keyword use await keyword in the body to wait for the result of an asynchronous code execution. If the result is received, then the next set of code executes and if it does not, then an error is caught by the catch block. This type of implementation eliminates the need to write separate callback functions to chain in an async request/response task execution.

JavaScript
<script>
    async function GET(url) {
        var result = await getResuts(url);
        return result;
    }
</script>

Async functions could not be used in browsers yet without transpiling the JS code using a suitable transpiler. A suitable replacement for async-await in JavaScript could be generator functions.

JavaScript
<script>
    function* someGennerator(url){
        yield get(url);
    }

    console.log(someGenerator('account/1').next().value);
</script>

Unfortunately, the support for generators is still very limited and it would be a while before we could see its widespread use in browsers.

The advanced implementations of promises in JavaScript has still a long way to go before things could become stable. But the good news is that things are advancing very fast or us. There is already support for such features in Node.js environment. For the browsers, there are several good transpilers out there like Babel which we could use to write and take advantage of the new syntax.

What I have seen over the past couple of years is that so many Devs are still not very clear about what promises are and how they could be leveraged not just in async server calls, but in so many different areas.

Custom Implementation

If you are feeling ambitious, then you can even try to implement your own promise object. I created a custom deferred object implemented some time back which supports chaining multiple callbacks:

I would advise to use a good promise library instead of writing our own implementations because it's risky to write custom code for such complicated specifications and it's always better to use code modules which are highly tested and have a widespread use and support by the developer community.

That is it for now folks. There is still a lot which could be covered on promises; if you are a newbie, then treat this article as a way to start off into working with async code in JavaScript. If you are an expert, then this might act as a refresher for you. Feel free to give meaningful comments or ask any question.

History

  • 26th June, 2016: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
India India
Just a regular guy interesting in programming, gaming and a lot of other stuff Smile | :)

Please take a moment to visit my YouTube Channel and subscribe to it if you like its contents!
My YouTube Channel

Don't be a stranger! Say Hi!!

Cheers!

Comments and Discussions

 
QuestionRead more about ES6 Promises Pin
Gerd Wagner27-Jun-16 2:44
professionalGerd Wagner27-Jun-16 2:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.