[JS#3 WIL 🤔 Post]
Most websites and applications written would, at some point request data from a server, wait for user input or in general do other processes that would take a good amount of time to finish. Because of this, Javascript supports asynchronous functions, simply, functions that can run in the background while other parts of the program execute. These functions are executed in its entirety when called, but might finish on some future time.
One, and the simplest way of achieving asynchrony is by using callbacks.
📌 Callbacks
A callback is a function passed as an argument to a different function. They are executed asynchronously or at a later time. Practically, programs are read from top to bottom, but this is not always the case as async code may run different functions at different times. For instance, when handling click events for buttons in an HTML form, we typically do this:
submitBtn.addEventListener("click",
//this function is a callback
function() {
printSomething();
}
);
function printSomething() {
console.log("Hello, Dev Community!");
}
In the snippet above, the addEventListener
function takes a callback, in this case the function invoking the printSomething
method. It will then be invoked when the submitBtn
is clicked.
Using callbacks are pretty easy and convenient when dealing with simple processes. However, it may quickly get out of hand if they are chained or nested deeply together, famously known as Callback Hell or pyramid of doom 😟.
So, callbacks are very fundamental in JS asynchrony. However, as the language grows and with program logic getting a little too complex, they are not enough. The future of JS requires a more sophisticated way of async patterns, one such mechanism is a promise
📌 Promises
A promise is an object that MIGHT produce a value at some future time. For example, let us say that the function fetchDatabaseData
gets data from a server and returns it as an object.
function fetchDatabaseData() {
//fetches data via some API
...
return api.getData();
}
Fetching the data from the server may take time, so unless we tell the code that, it will always assume that the data is ready. The api.getData()
is probably not yet finished, so, executing the below snippet may have an unwanted result, e.g. undefined
😣.
const apiData = fetchDatabaseData();
const firstEntry = apiData['first];
So, to fix this problem, we need a mechanism to tell our program to wait for fetchDatabaseData
to finish. Promises solve this issue for us.
To fix the above code, we can do something like,
function fetchDatabaseData() {
//fetches data via some API
...
return new Promise(function(resolve, reject) {
const result = api.getData();
if(result == undefined) {
reject("Ooops, we've got an error");
} else {
resolve(result);
}
});
}
const apiData = fetchDatabaseData();
apiData.then(
// handle fulfilled operation
function(data) {
const firstEntry = apiData['first'];
console.log(firstEntry);
},
//handle rejection error
function(err) {
console.error(err);
}
);
From the example above, a promise behaves as a 'future'
value. Since the time-dependent process is encapsulated inside the promise, the promise itself can be treated as time-independent. Meaning, it can be combined with any other promise regardless of how long the encapsulated process might take, without any problem.
When the result from the api is undefined
, the returned Promise will have a rejected state. Thus, the then
method would print "Oops, we've got an error"
.
On the other hand, if the api results to a defined object, the Promise would be fulfilled and the then
method would print the api results.
In addition, immutability is one of the most essential aspect of promises. Once a promise has been resolved, it stays that way FOREVER. It becomes immutable. You can then pass the promise around and know that it cannot be modified maliciously or accidentally.
I created a very simple weather application using the fetch function which returns a promise. The live preview and the code can be found in github.
I also recommend reading these chapters of You Don't Know JS
: Callbacks in Chapter 2 and Promises in Chapter 3
for a more in-depth discussion of these concepts.
In conclusion, there are a lot of ways to handle asynchrony in Javascript, two of them via callbacks or promise. Callbacks can be used in handling simple logic but might get out of hand if chained, i.e. callback hell. Promises do not get rid of callbacks, they try to address callback chains by expressing the asynchronous program flow in a sequential way. It helps in writing maintainable JS code. It also makes the code more readable as the program-flow is sequential, which is similar on how our brain plans and executes actions.
Cheers to continuous learning! 🍷
[REFERENCES]
[1] Art of Node
[2] The Odin Project: Async
[3] You Don't Know JS
Top comments (4)
@pat_the99 This article is good to get started with
Promises
, but not practical. The world has moved toasync - await
. Use them and put some practical code in the article. I believe it would be more helpful.async - await
truely seems like power in hands!Hi @vishalraje7 , yes will definitely study
async - await
when I get comfortable with the basics . Thank you for the suggestion 😄!Useful it was