Asynchronous Javascript
What is it?
A code is Asynchronous when it implements a function that start now and finish it later, and doesn't wait for the result of that function.
Usually, the code is synchronous: that means that every statement has to wait the last one be completed before moving on.
Example of synchronous code
console.log(1)
console.log(2)
console.log(3)
console.log(4)
What this code prints out is:
1
2
3
4
We can see that the output follows the same order that what we write in the example.
Example of asynchronous code
console.log(1)
console.log(2)
setTimeout(() => {
console.log('callback');
}, 2000);
console.log(3)
console.log(4)
The console output is:
1
2
3
4
callback
Different than what we expected, the program does not wait the return of the timeout to print callback
. It continues the program flux till the answear of the callback function is received, and then, print it out.
Callback
Callback is a function fired when and if you get the return result. In the example below, the callback function is fired after 2 seconds, so, is fired when timed out (not if, it will not stop counting except you close the browser page or if the world stops):
setTimeout(() => {
console.log('callback');
}, 2000);
Callback Hell
What if you want to set different timeouts for each output? I don't know why would you do that, but you can think in a code like this:
setTimeout(() => {
console.log(1);
}, 3000);
setTimeout(() => {
console.log(2);
}, 2000);
setTimeout(() => {
console.log(3);
}, 1000);
The output would be:
3
2
1
No! But it's not what you wanted. You want it to wait 3sec then display 1
, wait 2sec then display 2
, and finally wait 1sec and display 3
. To do so, you need to nest the callbacks:
setTimeout(() => {
console.log(1);
setTimeout(() => {
console.log(2);
setTimeout(() => {
console.log(3);
}, 1000);
}, 2000);
}, 3000);
For this code, the output is as expected:
1
2
3
But I hope you will agree that the code is really hard to read and understant. That's why these nested callbacks are called callback hell.
Promises
A Promise is a promise (?) that something you requested will happen. That means that you are not sure if a task it's going to be completed or not. Note that the original concept and the javascript concept are pretty much alike.
Let's create an situation:
You are a programmer (if you're here, I'm betting that this is right), and you don't understand promises yet. I'm promising you that you will understand it once you finish reading.
That's a promise, and it has 3 states:
- Pending: You don't know if you're going to learn promises
- Fullfilled: I'm proud of you and I'm gonna give you a cookie
- Rejected: I just lied
Writing this in JavaScript:
let liar = true;
let willLearnPromise = new Promise(
(resolve, reject) => {
if(liar){
let reason = new Error('yeah, I lied');
reject(reason);
}else{
let cookie = "I'm giving you a cookie!";
resolve(cookie)
}
}
)
I believe this code is really simple to understand. But, as a formality, I'll explain the Promise
structure.
A Promise
takes as a parameter a function, and in that function, we automatically get access to 2 parameters: resolve
and reject
.
The resolve
parameter is used to show that what we expected happened, and the reject
parameter is used to show that something went wrong.
The basic Promise structure is:
new Promise((resolve, reject) => {
if(everything goes right)
resolve();
if(an error ocurred){
reject();
}
});
Consuming Promises
To consume our Promise
, I'll do the following:
let willLearnPromise = ... //here goes the code seen before
//call our promise
let readContent = () => {
willLearnPromise
.then(fullfilled => { console.log(fullfilled) })
.catch(rejected => { console.log(rejected) })
}
readContent();
When we consume a promise, we use then
to get the resolve
, and catch
, to get the reject
.
You can merge the codes and run it in your browser.
If you do, the output will be an error:
Error: yeah, I lied
And no! I'm not a liar. I needed it to be outputed because I don't know how to deliver you a cookie. But I promise
I will! So, let's se how.
Chaining Promises
As I said before, I promise
you that I'll deliver you a cookie.
let deliverCookie = () => {
return new Promise((resolve, reject) => {
resolve("The cookie is on it's way!");
})
}
Note that we are only resolving the promise
. In that cases, the code can be written as below:
let deliverCookie = () => {
return Promise.resolve("The cookie is on it's way!");
}
The deliverCookie
promise can only start after the willLearnPromise
.
let liar = false;
let willLearnPromise = ... //here goes the code seen before
let deliverCookie = ... //here goes the code seen before
let readContent = () => {
willLearnPromise
.then(deliverCookie)
.then(fullfilled => { console.log(fullfilled) })
.catch(rejected => { console.log(rejected) })
}
readContent();
Now, you can run this code and fulfill it with the previous code snippets provided above.
The output will be:
The cookie is on it's way!
And here it is: :cookie:
Async and await
Consider the following code:
fetch('https://jsonplaceholder.typicode.com/todos')
.then(response => response.json())
.then(json => console.log(json))
.catch(err => console.log(err))
In here, we're using Promises chaining. But imagine a scenario where inside the then
you have to wait for another promise, that'll generete another then
and so on. It would be a lot of chaining.
To avoid that, we can use async
to use await
keyword inside to chain promises in a better way.
const getTodos = async () => {
let response = await fetch('https://jsonplaceholder.typicode.com/todos');
let data = await response.json();
return data;
}
getTodos().then(data => console.log(data));
First, we put all the code we want inside a function, that will be async
. In this case, it'll be getTodos
. Rememer that this function returns a promise, so when getTodos
is called, we still going to need the then
method.
Inside an async
function, we can treat our Promises as non blocking code by writing the await
keyword. No need for chaining promises and the code get's a lot more readable.
How to learn more
Useful Material
- https://www.digitalocean.com/community/tutorials/javascript-promises-for-dummies#understanding-promises
- https://www.luiztools.com.br/post/programacao-assincrona-em-nodejs-callbacks-e-promises/
- https://javascript.info/async-await
- https://bigcodenerd.org/resolving-promises-sequentially-javascript/
- https://www.youtube.com/watch?v=CWjNefiE47Y&list=PL4cUxeGkcC9jx2TTZk3IGWKSbtugYdrlu&index=10
- https://www.youtube.com/watch?v=vn3tm0quoqE