Those of us who have dig deeper into Javascript will know that this is a single thread, which means that it can only do one job at a time. However, imagine that, when we call the API and while waiting for the result to return, the main thread gets blocked so the site cannot respond or interact with the user, until the API call has completed. socks. That is why the concept of asynchronous (asynchronous) appears in Javascript. There are 3 ways to do asynchronous implementation in Javascript: callbacks, Promises, and async / await. Let’s find out together!
Callback
In javascript, we can pass a function as an argument of another function, which is the callback. First of all, we will look at an example using the following callback:
1 2 3 4 5 6 7 8 9 | <span class="token keyword">function</span> <span class="token function">doHomework</span> <span class="token punctuation">(</span> <span class="token parameter">subject <span class="token punctuation">,</span> callback</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">alert</span> <span class="token punctuation">(</span> <span class="token template-string"><span class="token template-punctuation string">`</span> <span class="token string">Starting my </span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> subject <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string"> homework.</span> <span class="token template-punctuation string">`</span></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">callback</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">alertFinished</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">alert</span> <span class="token punctuation">(</span> <span class="token string">'Finished my homework'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">doHomework</span> <span class="token punctuation">(</span> <span class="token string">'math'</span> <span class="token punctuation">,</span> alertFinished <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
We can see that in the doHomework
function, we pass the second parameter as another function and will be called inside the function that wraps it, the callback
function. The code above will “fire” two alerts, which is Starting my math homework.
, then comes Finished my homework
. This example is very easy to understand, right.
I will take one more example of using the callback to solve the problem of calling request and waiting for the response to return as I mentioned at the beginning of the post:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token keyword">function</span> <span class="token function">request</span> <span class="token punctuation">(</span> <span class="token parameter">url <span class="token punctuation">,</span> callback</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> xhr <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> xhr <span class="token punctuation">.</span> timeout <span class="token operator">=</span> <span class="token number">2000</span> <span class="token punctuation">;</span> xhr <span class="token punctuation">.</span> <span class="token function-variable function">onreadystatechange</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token parameter">e</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> xhr <span class="token punctuation">.</span> readyState <span class="token operator">===</span> <span class="token number">4</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> xhr <span class="token punctuation">.</span> status <span class="token operator">===</span> <span class="token number">200</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">callback</span> <span class="token punctuation">(</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> xhr <span class="token punctuation">.</span> response <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">callback</span> <span class="token punctuation">(</span> xhr <span class="token punctuation">.</span> status <span class="token punctuation">,</span> <span class="token keyword">null</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> xhr <span class="token punctuation">.</span> <span class="token function-variable function">ontimeout</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">'Timeout'</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> xhr <span class="token punctuation">.</span> <span class="token function">open</span> <span class="token punctuation">(</span> <span class="token string">'get'</span> <span class="token punctuation">,</span> url <span class="token punctuation">,</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> xhr <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
The above request
function has one parameter, which is the callback function. This function will be called when the request is successfully created ( callback(null, xhr.response)
) or fails ( callback(xhr.status, null)
).
The callback function is very easy to understand and use, however, to perform complex code, the callback shows its weakness, which is callback hell
, it will make our code very bad, difficult to read and difficult to maintain. For a very long time, we had to rely on callbacks to work with asynchronous code in javascript. As a result, many of us have had terrible experiences dealing with functions that look like this:
Promises
With promises, you can write asynchronous code that is easier to read and maintain. To use promises, we declare with the new Promise
keyword:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token keyword">const</span> myPromise <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token parameter">resolve <span class="token punctuation">,</span> reject</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// code here</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> codeIsFine <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">resolve</span> <span class="token punctuation">(</span> <span class="token string">'fine'</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">reject</span> <span class="token punctuation">(</span> <span class="token string">'error'</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> myPromise <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token function">whenOk</span> <span class="token punctuation">(</span> <span class="token parameter">response</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> response <span class="token punctuation">)</span> <span class="token keyword">return</span> response <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">catch</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token function">notOk</span> <span class="token punctuation">(</span> <span class="token parameter">err</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> err <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> |
Let’s analyze the above code:
- A promise is initialized with the
new
keyword, withresolve
andreject
as two parameters passed. - Inside the Promise function,
resolve
will be called when the code is executed correctly, otherwisereject
will be called. - When
resolve
is called, the code inside the.then
function is executed, and withreject
, the code inside.catch
is executed.
In order to properly use Promises, we need to note the following:
resolve
andreject
only accept one parameter even if you pass the tworesolve('yey', 'works')
parameters, they only accept the first parameter as ‘yey’ and pass that parameter to the callback function in.then
- If you want to use
.then
serial.then
need to addreturn
otherwise the value of next.then
call will beundefined
- When using
.then
succession, if an error occurs, the code will skip executing the next.then
function until the.catch
appears. - A Promise has 3 statuses: pending while waiting for a
resolve
orreject
be called, to be resolved and rejected . Once the Promise isresolved
orrejected
, it cannot be changed anymore.
Async / await
Async / await is a feature for working with the last asynchronous functions I want to talk about. Compared to callbacks and Promises, async / await will make our code easier to understand and much more interesting. Async / await is built on Promises and is compatible with all API-based Promises.
First, let’s learn about async / await concepts:
- Async : The
async
keyword is equivalent to declaring anew Promise
- Automatically converts an ordinary function into a Promise.
- When the async function is called, it will process everything and return the result in its function.
- Async allows to use Await.
- Await : As its name suggests : “Wait a minute”
- When pre-booking a Promise, it waits until the Promise ends and returns results.
- Await works only with Promises, it doesn’t work with callbacks or works alone.
- Await can only be used inside async functions.
I will take the example of sending the request as above, but do it by async / await:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <span class="token keyword">function</span> <span class="token function">request</span> <span class="token punctuation">(</span> <span class="token parameter">url</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token parameter">resolve <span class="token punctuation">,</span> reject</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> xhr <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> xhr <span class="token punctuation">.</span> <span class="token function-variable function">onreadystatechange</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token parameter">e</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> xhr <span class="token punctuation">.</span> readyState <span class="token operator">===</span> <span class="token number">4</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> xhr <span class="token punctuation">.</span> status <span class="token operator">===</span> <span class="token number">200</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">resolve</span> <span class="token punctuation">(</span> xhr <span class="token punctuation">.</span> response <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">reject</span> <span class="token punctuation">(</span> xhr <span class="token punctuation">.</span> status <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> xhr <span class="token punctuation">.</span> <span class="token function-variable function">ontimeout</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">reject</span> <span class="token punctuation">(</span> <span class="token string">'timeout'</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> xhr <span class="token punctuation">.</span> <span class="token function">open</span> <span class="token punctuation">(</span> <span class="token string">'get'</span> <span class="token punctuation">,</span> url <span class="token punctuation">,</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> xhr <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
We will make the request send:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">list</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> userGet <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span> <span class="token string">https://api.github.com/search/users?page=1&q=daspinola&type=Users</span> <span class="token template-punctuation string">`</span></span> <span class="token keyword">const</span> users <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">request</span> <span class="token punctuation">(</span> userGet <span class="token punctuation">)</span> <span class="token keyword">const</span> usersList <span class="token operator">=</span> <span class="token constant">JSON</span> <span class="token punctuation">.</span> <span class="token function">parse</span> <span class="token punctuation">(</span> users <span class="token punctuation">)</span> <span class="token punctuation">.</span> items usersList <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token parameter">user</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> repos <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">request</span> <span class="token punctuation">(</span> user <span class="token punctuation">.</span> repos_url <span class="token punctuation">)</span> <span class="token function">handleRepoList</span> <span class="token punctuation">(</span> user <span class="token punctuation">,</span> repos <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">handleRepoList</span> <span class="token punctuation">(</span> <span class="token parameter">user <span class="token punctuation">,</span> repos</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> userRepos <span class="token operator">=</span> <span class="token constant">JSON</span> <span class="token punctuation">.</span> <span class="token function">parse</span> <span class="token punctuation">(</span> repos <span class="token punctuation">)</span> <span class="token comment">// Handle each individual user repo here</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> user <span class="token punctuation">,</span> userRepos <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
In the above code, the request(userGet)
function will be executed, when the response is successful, it will be assigned to the users
variable, then the code below will be executed. Through the above example we can see that using async / await makes our code look like normal synchronous code, very easy to read and understand compared to callbacks or Promises.
Conclude
All 3 methods I introduced above have their own advantages, so let’s use them most effectively depending on the target and different contexts. Proficient and efficient use of these methods will make maintaining code a lot easier.
References :
https://medium.com/free-code-camp/javascript-from-callbacks-to-async-await-1cc090ddad99
https://topdev.vn/blog/giai-thich-ve-async-await-javascript-trong-10-phut/