I recently read up on JavaScript Promises
and Sasha Vodnik’s “JavaScript: Async” helped me better understand its history and current usage.
Table of Contents
- Table of Contents
- Synchronous and asynchronous programming
- Ways to implement asynchronous programming in JS
- References
Synchronous and asynchronous programming
There are two ways code can be executed in JS:
-
Synchronous refers to running each line of code and waiting for them to finish before the next. This idle time is also refered to as “blocking”.
This is common for steps that need to be executed chronologically and depends on its predecessor.
-
Asynchronous on the other hand does not wait for other lines of code to finish execution. They are immediately executed and JS provides ways to catch their respective responses after they’re done.
Async code do not need others to execute independently hence the convenience of no blocking occurrences in its flow.
Ways to implement asynchronous programming in JS
XMLHttpRequest
This is the earliest way to communicate with server API with support starting from IE7. It is closely used with the AJAX (asynchronouse JavaScript and XML) technology.
let httpRequest = new HttpRequest();
httpRequest.open('GET', url);
httpRequest.onload = () => {
if (httpRequest.status === 200) {
successHandler(httpRequest.responseText);
} else {
failHandler(httpRequest.status)
}
}
httpRequest.send();
In here I checked the response status in order to implement handling of successful and failed communication.
Promises
Starting in ES6, the Promises
was shipped to browsers that enabled a new way to handle asynchronous acceptance (resolve
) or rejection (reject
) of responses.
function get(url) {
return new Promise((resolve, reject) => {
...
resolve(response);
...
reject(response);
})
}
get(url)
.then(response => {
successHandler(response);
})
.catch(status => {
failHandler(status);
})
.finally(() => {
...
});
Before I thought that the resolve
and reject
objects were direct equivalents of the successHandler
and failHandler
respectively from my examples and that the latter should substitute the first in code. Fortunately I now know better.
Chainable instance methods
then()
- handles
resolve
cases and can be chained to otherthen()
s to pass response objects.
- handles
catch()
- handles
reject
cases.
- handles
finally()
- executes after the first two regardless of the result.
Promise.all
In real projects we need to deal with multiple fetches of data in bulk! Good thing Promise.all
is available for use.
It just takes an array of Promise
objects and will resolve them in the chains.
Promise.all(urlArray.map(url => get(url)))
.then(responses => {
return responses.map(response =>
successHandler(response);
);
})
.then(data => {
...
})
.catch(status => {
failHandler(status);
})
.finally(() => {
...
});
- Expect that it will return an array of responses and will need specific case handling based on your logic.
- In my example there is a second
then()
with thedata
parameter. This represents an array of the individually-processed response objects in the initialthen()
.
async
/await
These two keywords are applied to functions that deal with asynchronous code or basically has Promise
s inside.
async
is attached before thefunction
declaration.await
is attached before methods that return aPromise
to be resolved, usually during assignment. This keyword is only permitted in a method that is declaredasync
and can be used multiple times.
In order to not mix
Promise.all
here I declared the individualget()
calls to show only the two keywords in use.
(async function() {
try {
let responses = [];
responses.push(await get(urlArray[0]));
...
responses.push(await get(urlArray[n]));
let data = response.map(response =>
successHandler(response);
);
...
} catch (status) {
failHandler(status);
} finally {
...
}
})();
Instead of the chained methods in Promise
, the familiar try
, catch
and finally
pattern is used here.
Dealing with array comprehension
We can mix these keywords with Promise.all()
to take advantage of the new array methods! The code before can be simplified to:
(async function() {
try {
let responses = await Promise.all(urlArray.map(url => get(url)));
let literals = responses.map(response => {
return successHandler(response);
});
...
I initially tried to do the following code but the push
method is synchronous so it will fail to wait for the get()
process.
urlArray.map(url => {
responses.push(await get(url));
});
References
- Ajedi32. “Use Async Await with Array.map.” Stack Overflow, Stack Overflow, 19 Oct. 2016, <
https://stackoverflow.com/a/40140562/3314071
>. - Elliott, Eric. “Master the JavaScript Interview: What Is a Promise?” Medium, JavaScript Scene, 2 July 2019, <
https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261
>. - Kantor, Ilya. “Async/Await.” The Modern JavaScript Tutorial, The Modern JavaScript Tutorial, 3 Feb. 2020, <
https://javascript.info/async-await
>. - “Promise.prototype.catch().” MDN Web Docs, Mozilla Development Network, 2020, <
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch
>. - Vodnik, Sasha. “JavaScript: Async.” LinkedIn Learning, LinkedIn Learning, 14 Oct. 2019, <
https://linkedin.com/learning/javascript-async/
>. - “XMLHttpRequest.” MDN Web Docs, Mozilla Development Network, 2020, <
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
>.