This is an authorized translation of @Mohsan Riaz’s medium post. See portal
Writing asynchronous code in Javascript is often disconcerting, especially when using the continuation-passing style(CPS) style. Asynchronous code affects the readability of code, making it harder to decouple into smaller, separate blocks of code, making it more error-prone and harder to change later.
Continuation -passing style: Continuation -passing style: Continuation -passing style: Continuation -passing style: Continuation -passing style: Continuation -passing style: Continuation -passing style: Continuation -passing style: Continuation -passing style: Continuation -passing style: Continuation -passing style: Continuation -passing style See this blog for details
If there was a way to write asynchronous code in a similar style to synchronous code, our programming lives would be much more enjoyable. Promises using JavaScript can do this.
Let’s start by trying to figure out what’s wrong with using callback.
Callbacks
For example, if you need to get a list of countries, then get the list of cities in the first country in the list of countries, then get all the universities in the list of cities, and finally show the first university in the first city.
Because each call depends on the results of the previous call, a series of nested callback functions are called.
function fetchCountries(){
fetchJSON("/countries".(success, countries) = > {
if(success){
try{
// do some stuff
fetchCities(countries[0].id);
} catch(e){ processError(); }}else
processError();
});
}
function fetchCities(countryId){
fetchJSON(`countries/${countryId}/cities`.(success, cities) = > {
if(success){
try{
// do some stuff
fetchUniversities(cities[0].id);
} catch(e){
processError()
}
}else
processError();
});
}
function fetchUniversities(cityId){
fetchJSON(`cities/${cityId}/universities`.(success, universities) = > {
if(success){
try{
// do some stuff
fetchUniversityDetails(universities[0].id);
}catch(e){ processError(); }}else
processError();
});
}
function fetchUniversityDetails(univId){
fetchJSON(`/universities/${univId}`.(success, university) = > {
if(success){
try{
// do some stuff
hideLoader();
}catch(e){ processError(); }}else
processError();
});
}
Copy the code
What’s wrong with the above implementation?
Instead of putting asynchronous logic in the same place, the callback function decides which function to call next. In short, we give the functions a flow of control that tightly couples them together.
Why Promise
In my opinion, promises have four major advantages over a simple continuation-passing style:
- Better definition of asynchronous logic control flow
- The decoupling
- Better error handling
- Improved readability
The use of the Promise
Javascript promises tell asynchronous code to return a value like synchronous code, which is an object that promises success or failure. This small change makes it very powerful to use.
get("/countries")
.then(populateContDropdown)
.then(countries= > get(`countries/${countries[0].id}/cities`))
.then(populateCitiesDropdown)
.then(cities= > get(`cities/${cities[0].id}/universities`))
.then(populateUnivDropdown)
.then(universities= > get(`universities/${universities[0].id}`))
.then(populateUnivDetails)
.catch(showError)
.then(hideLoader)
Copy the code
Similar to synchronous programming, the output of one function is the input of the next. JS functions are used in the call chain as with json.parse, and their return values are fed to the next callback function.
If there is an exception, the catch function will catch it. The following code will run as usual, and the loader in the above example will be hidden after execution.
contrast
We define all asynchronous logic in the same place, without having to do extra checking or use try/catch for error handling. As a result, the code has higher readability, lower coupling, and reusable independent functions.
Error handling details
Whenever an intentional or unintentional exception occurs, it is thrown in the next catch handler. You can place a catch handler anywhere in the chain to catch a particular exception and display an error or revalue.
get("/countries")
.then(JSON.parse)
.catch(e= > console.log("Could not parse string"))
.then(...)
.catch(e= > console.log("Error occured"))
Copy the code
In the example above, the second console statement will not be printed.
If an exception has been caught and you want to pass it to the next catch handler, it must be thrown.
This is handy if you want to display multiple error messages, for example:
get("/coutries")
.then(...)
.catch(e= > {
showLowLevelError();
throw e;
})
.catch(showHighLevelError)
Copy the code
Promise vs Event listener
Event listeners are useful if an event is expected to be fired more than once, such as a button click event. Promises are similar to event listeners, but they differ in two essential ways:
- Promise will only be resolved once, whether it’s abnormal or normal.
- Even if the callback function added after the Promise decision is still called, this means you can check later that the Promise is
fullfiled
orrejected
.
This is useful because we are more interested in reacting to outcomes that occur. Here’s a simple example of image preloading:
var imagePromise = preloadImage("src.png");
setTimeout(() = > {
imagePromise.then(() = > console.log("image was loaded") )
.catch(() = > console.log("could not load the image"))},2000)
function preloadImage (path) {
return new Promise((resolve, reject) = > {
var image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path;
});
};
Copy the code
Promise.all([…] ) is useful for bulk resolution
Promise.all([imagePromise1, imagePromise2, ....] ) .then(...) .catch(...)Copy the code
conclusion
In most JavaScript practices, promises are very useful, especially if the success or failure callback can only be executed once. But in some practices, especially those where event callbacks are called multiple times, plain callbacks are better. Thank you for reading this article, and if you have any questions, please leave them in the comments section.
First translation, if you have any questions welcome to correct.