Javascript

Demonstrating Promises, Async, Await in Javascript

javascript promises async await

Promises in javascript are a mechanism to get the result from asynchronous based operations in a more sophisticated approach.

 

 

Overview

In modern web applications you already dealt with situations when you get results from asynchronous operations such as getting a response from a web service using ajax, this operation requires that you wait some time until you get the required response.

So recently to get results from an asynchronous operation you may have used the callback style approach, where you pass an argument to an asynchronous function and call that function inside.

 

Imagine for example to get results  from a certain endpoint with jquery ajax you may have written code like this:

function sendRequest(callback)
{
    $.ajax({
      .....
      success: function(response) {
          callback(response);        // the place to invoke callback
      }
    });
}

Now to get the actual results:

sendRequest(function(response) {
   // do something with the response
}); 

This the old style syntax to get the result from an asynchronous requests.

Fortunately Ecmascript 2015 had a new feature called Promises, which can be used to get results from async operations without depending in the callback method.

 

Terminology

To define a promise initialize a new promise instance like this:

let promise1 = new Promise(function(resolve, reject) {
     ...
});

As shown we created a new promise instance using new Promise(), the promise constructor takes a function with two parameters (resolve and reject). Those two parameters are functions used to emit values to the calling code depending on specific logic for example:

// call resolve or reject depending on specific logic

     if(true) {
        resolve('success');
     } else {
        reject('fail');
     }

The resolve() function designates that an operation completes successfully and the reject() designates that an operation failed such as an error happened, it’s up to the promise creator to identify what values the resolve and reject should return.

 

Javascript Promise states

Javascript Promises passes along various states across it’s life cycle:

  • Pending: meaning that the promise starting to execute.
  • Fulfilled: meaning that the promise completes successfully, resolve() function fired.
  • Rejected: meaning that the promise failed, reject() function fired().

An ideal javascript promise life cycle starts with pending then fulfilled or pending then rejected, it can’t have the three states altogether, as the operation might be end successfully of with failure.

 

Executing Promises

Now we seen how to define a promise, let’s explore how to execute or in other means listen for that promise and get results.

 

To get results from promise we call the “then()” and “catch()” function on the promise instance like this:

promise1.then(function(val) {
  console.log(val);
}).catch(function(val) {
  console.error(val);
});

As shown above the then and catch functions takes a callback with a value parameter, this is the value fired by either resolve() or reject() when you define the promise.

So if the the function resolve() executed the corresponding then() function is called to get the results and if the function reject() executed the corresponding catch() function called to get the error message.

 

Example

Let’s implement a promise based function to get results with xmlhttp, i will the jsonPlaceholder api endpoint to read json data:

function sendHttpRequest()
{
       return new Promise((resolve, reject) => {
             const xhr = new XMLHttpRequest();
    	     xhr.open("GET", "https://jsonplaceholder.typicode.com/todos");
    	     xhr.onload = () => resolve(xhr.responseText);
    	     xhr.onerror = () => reject(xhr.statusText);
    	     xhr.send();

	});
}

sendHttpRequest().then(response => console.log(response))
      .catch(error => console.error(error));

As shown in the code we used the XMLHttpRequest to fetch remote data along with the promises, then i called resolve and reject accordingly in case there is success or error occurred.  Note that i used here the function expressions and arrow functions to make the code more shorter like this:

return new Promise((resolve, reject) => {
....
});

... 

sendHttpRequest().then(response => console.log(response));

 

Chaining Promises

Sometimes in the old callback approach the need comes to execute a nested callback depending on the result of the previous callback and so on but this leads to the so called pyramid of doom like this:

sendRequest(function(response) {
  formatResponse(response, function(formatted) {
    displayResponse(formatted, function(result) {
        .....
    }, errorCallback);
  }, errorCallback);
}, errorCallback);

 Javascript promises overcome this problem by chaining multiple promises to be executed in sequential order with the result of the first promise assigned to second promise and so on. So converting the previous code to promise chain like this:

sendRequest().then(function(response) {
   return formatResult(response);
}).then(function(formatted) {
   return displayResult(formatted);
}).then(function(result) {
   // ....
}).catch(function(error) {
   // ....
});

Let’s rewrite the previous example to include another promise for formatting the result and another for displaying it:

<div id="result"></div>


<script>

function sendHttpRequest()
		{
			return new Promise((resolve, reject) => {

				const xhr = new XMLHttpRequest();
    			xhr.open("GET", "https://jsonplaceholder.typicode.com/todos");
    			xhr.onload = () => resolve(xhr.responseText);
    			xhr.onerror = () => reject(xhr.statusText);
    			xhr.send();

			});
		}

		function formatResult(data) 
		{
			return new Promise(resolve => resolve(JSON.parse(data)) );
		}

		sendHttpRequest()
			.then(response => formatResult(response))
			.then(results => {
				let html = "<table><tr><td><strong>Title</strong></td><td><strong>Completed</strong></td></tr>";
				
				results.map(data => {

					html += `<tr><td>${data.title}</td><td>${data.completed}</td></tr>`;
				})

				html += "</table>";

				document.getElementById("result").innerHTML = html;
			})
			.catch(error => console.error(error));

</script>

In the above code i added another function that return a promise, this promise actually depends on the result of the first promise returned from function “sendHttpRequest“, to get results from those promises i chain them together like this:

sendHttpRequest()
	.then(response => formatResult(response))
	.then(...);

In other words the first then() function return a promise, to execute that promise you attach another then() function and so on until all next promises execute and get the final result.



Resolving and Rejecting Promises Manually

There are two shortcuts functions that enable you to resolve and reject promises manually without using the Promise() constructor.

Promise.resolve(value)   // resolve the promise
Promise.reject(value)    // reject the promise

Those functions used when you are sure that the promise is resolved or rejected automatically. You can think of those functions as they used to execute synchronous operations. Both functions return a promise instance that need to be fulfilled with “then()” and “catch()”.

 

Promise.resolve()

const p1 = Promise.resolve(21);

p1.then(function(val) {
     console.log(val);
});

Promise.reject()

const p2 = Promise.reject('error');

p2.catch(function(val) {
    console.error(val);
});

 

Running Multiple Promises in Parallel

Javascript promises supports a way to run multiple promises in parallel as a single promise. To accomplish that the javascript promise Api provides two functions Promise.all() and Promise.race(). Both functions takes an array of promise instances and executes them all and return.

var promise1 = new Promise((resolve, reject) => {
    	setTimeout(() => resolve('first promise'), 500);
});

var promise2 = new Promise((resolve, reject) => {
    	setTimeout(() => resolve('second promise'), 100);
});

Promise.all([promise1, promise2]).then((value) => {
  	console.log(value[0], value[1]);
});

As shown in the code above Promise.all() takes an array of promises, and the result of calling then() will also be array of the results of both promises.

 

Promise.race() is like Promise.all() is that it takes an array of promises except that instead of returning all promises results, it only return the first promise in the queue that it’s result comes first. Let’s change Promise.all() in the previous example to Promise.race().

var promise1 = new Promise((resolve, reject) => {
    	setTimeout(() => resolve('first promise'), 500);
});

var promise2 = new Promise((resolve, reject) => {
    	setTimeout(() => resolve('second promise'), 100);
});

Promise.race([promise1, promise2]).then((value) => {
  	console.log(value);
});

As shown if you run this code you will see that the second promise result returned only as that setTimeout after 100 ms. Note also the “then()” value is a single value not an array in contrast with Promise.all().



async / await

A related topic to Promises is the async/await functions. The async keyword used to define an asynchronous function, this async function is much more like a normal function but instead it return an implicit Promise object.

The await keyword is used inside the async function to get the result of promise based function call.

 

If you tried to use await inside non async function you will get an error similar to this:

SyntaxError: await is only valid in async functions and async generators

 

Let’s see this example

function sayHello() {
  	return new Promise(resolve => {
    		setTimeout(() => {
      			resolve('hello');
    		}, 2000);
  	});
}

async function display()
{
	var result = await sayHello();

	console.log(result);
}

display();

In the above code the function sayHello() return a promise instance, so to execute that promise as you learned above you may call “then()” instead i used the async/await keywords as an alternative to calling “then()“.

So i defined another function and declared it as async which means it returns data from asynchronous function call and i added the keyword await before sayHello(). await() means wait until the result from that promise is available and assign it variable result.

If you omit the await keyword you will instead get a promise object not the actual value.

0 0 votes
Article Rating

What's your reaction?

Excited
0
Happy
0
Not Sure
0
Confused
0

You may also like

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments