Are you a fan of the async
/ await
syntax? Me too, in my opinion async
/ await
is better than Promise chains
. But I wonder if you have used it properly. Sometimes it can make your program run slower than expected. In this article, I will share something very important if you want to improve performance when using async
/ await
and that is extremely easy to apply in your project.
Before Reading
Many JavaScript Devs love the async
/ await
syntax. I think the reason why people like this syntax is because Promise hell
, it keeps creating a chain
when multiple then
dots in a row.
async
/ await
allows you to reduce or get rid of Promise hell
by using the await
keyword. But have you ever thought that this syntax can slow down your application when it is used in a non-optimal way?
So we have to do? Let’s talk about our options.
Helper Functions
Suppose there is a function called sleep
. It will wait for the amount of time you want and is a basic Promise
.
1 2 3 4 5 6 | <span class="token keyword">const</span> <span class="token function-variable function">sleep</span> <span class="token operator">=</span> <span class="token parameter">t</span> <span class="token operator">=></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 parameter">res</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setTimeout</span> <span class="token punctuation">(</span> res <span class="token punctuation">,</span> t <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> <span class="token punctuation">}</span> <span class="token punctuation">;</span> |
And we also have a fetch
. function
1 2 3 4 5 | <span class="token keyword">const</span> <span class="token function-variable function">fetch</span> <span class="token operator">=</span> <span class="token parameter">url</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> time <span class="token operator">=</span> url <span class="token punctuation">.</span> length <span class="token operator">*</span> <span class="token number">1000</span> <span class="token punctuation">;</span> ngủ trở <span class="token function">lại</span> <span class="token punctuation">(</span> thời gian <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> |
The fetch
pseudo-function takes an argument of type string
and it is used to calculate the timeout. If we call fetch('/notice')
, the timeout will be seven seconds because /notice
has seven characters.
The basic situation and problem encountered for this example are
Now, we access the application; it is probably /
. As soon as we access the main page, the code will try to load the data by typing fetch
. (In this example, I use React
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token comment">// React base</span> <span class="token keyword">async</span> <span class="token function">componentDidMount</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> banners <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">'/banners'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> events <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">'/events'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> notices <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">'/notices'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">/* Do other tasks here */</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">setState</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> state <span class="token punctuation">,</span> banners <span class="token punctuation">,</span> events <span class="token punctuation">,</span> notices <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
We’ve done a fetch
function before, which is a Promise
. It has a string argument.
/banners
→ eight characters/eight second delay/events
→ seven characters/seven seconds delay/notices
→ eight characters/eight second delay
The total fetch
time will be 23 giây
because it waits for the current fetch
to finish before calling the next one.
This structure is fine if fetch
order is important. For example, we should fetch
/login
before /my-profile
because that is members-only information.
What if there are more than five API
that you need to fetch before rendering? The more APIs, the longer your users will wait before viewing the page.
Solution
The solution is very clear and simple.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token comment">// React base</span> <span class="token keyword">async</span> <span class="token function">componentDidMount</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> bannersFetch <span class="token operator">=</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">'/banners'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> eventsFetch <span class="token operator">=</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">'/events'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> noticesFetch <span class="token operator">=</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">'/notices'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">/* Do other tasks here */</span> <span class="token keyword">const</span> banners <span class="token operator">=</span> <span class="token keyword">await</span> bannersFetch <span class="token punctuation">;</span> <span class="token keyword">const</span> events <span class="token operator">=</span> <span class="token keyword">await</span> eventsFetch <span class="token punctuation">;</span> <span class="token keyword">const</span> notices <span class="token operator">=</span> <span class="token keyword">await</span> noticesFetch <span class="token punctuation">;</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">setState</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> state <span class="token punctuation">,</span> banners <span class="token punctuation">,</span> events <span class="token punctuation">,</span> notices <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
The only difference from the previous version is that we no longer put the await
keyword before fetch
. Instead, we put it later. What happens after that?
Each fetch
request will be sent to the server because none of them wait for a response before triggering the next fetch
request.
Even though there is no extra work in componentDidMount
in the example above, the longest maximum timeout would be just eight seconds.
Promise brings miracles
The reason that we can get huge benefit from changing a few lines of code is because of Promise
. Basically, Promise
is known as an API
that works asynchronous
. However, the real secret is hidden in the event queue.
JavaScript
is single-threaded
, as many of you already know. That means there is only one task running in the program at a time. Container, which is a type of queue that holds tasks until their turn is executed, is known as event queue or task queue this can vary depending on the document you are reading.
Examples of tasks that might be considered a normal task are console.log(1)
, obj.foo()
or anything similar that is not an asynchronous
job.
Promise
is an API asynchronous
. At runtime, JavaScript
sends a Promise
task (or a Promise
-based task) to the Promise
event queue. And the tasks in the Promise
queue must also wait their turn to be called. But note that any task in the Promise
queue can only be pulled out and run if the normal task
is completely empty.
1 2 3 4 | <span class="token keyword">const</span> banners <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">'/banners'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> events <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">'/events'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> notices <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">'/notices'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Due to the await
keyword on each line, the Promise
queue cannot accept more tasks. First, fetch('/banners')
is executed. The program waits for its response
and queues the next fetch
.
1 2 3 4 5 6 7 8 | <span class="token keyword">const</span> bannersFetch <span class="token operator">=</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">'/banner'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> eventsFetch <span class="token operator">=</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">'/events'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> noticesFetch <span class="token operator">=</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">'/notices'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> banners <span class="token operator">=</span> <span class="token keyword">await</span> bannersFetch <span class="token punctuation">;</span> <span class="token keyword">const</span> events <span class="token operator">=</span> <span class="token keyword">await</span> eventsFetch <span class="token punctuation">;</span> <span class="token keyword">const</span> notices <span class="token operator">=</span> <span class="token keyword">await</span> noticesFetch <span class="token punctuation">;</span> |
With this code, all three fetch
can be viewed as stacked in the Promise
queue in order from top to bottom. Those Promise
will be pulled out and executed at roughly the same time (when the normal task
queue is empty), but they don’t have to wait for the response of the previous fetch
.
Demo
<iframe src=”https://codesandbox.io/embed/async-await-performance-vmo2q?fontsize=14&hidenavigation=1&theme=dark” style=”width:100%; height:500px; border:0; border-radius : 4px; overflow:hidden;” title=”async-await-performance” allow=”accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking” sandbox= “allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts” ></iframe>
Another example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | <span class="token keyword">const</span> <span class="token function-variable function">fetchA</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></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 punctuation">(</span> <span class="token parameter">callback</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setTimeout</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">callback</span> <span class="token punctuation">(</span> <span class="token string">"OK"</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token number">3000</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> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">fetchB</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></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 punctuation">(</span> <span class="token parameter">callback</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setTimeout</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">callback</span> <span class="token punctuation">(</span> <span class="token string">"OK"</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token number">2000</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> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">fetchC</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></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 punctuation">(</span> <span class="token parameter">callback</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setTimeout</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">callback</span> <span class="token punctuation">(</span> <span class="token string">"OK"</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token number">1000</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> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">runFetch</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> startTime <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getTime</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token constant">A</span> <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetchA</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token constant">B</span> <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetchB</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token constant">C</span> <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetchC</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> result <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token constant">A</span> <span class="token punctuation">,</span> <span class="token constant">B</span> <span class="token punctuation">,</span> <span class="token constant">C</span> <span class="token punctuation">,</span> time <span class="token operator">:</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">Date</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getTime</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> startTime <span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">1000</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">"runFetch :>> "</span> <span class="token punctuation">,</span> result <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">runBetterFetch</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> startTime <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getTime</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> fetch1 <span class="token operator">=</span> <span class="token function">fetchA</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> fetch2 <span class="token operator">=</span> <span class="token function">fetchB</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> fetch3 <span class="token operator">=</span> <span class="token function">fetchC</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token constant">A</span> <span class="token operator">=</span> <span class="token keyword">await</span> fetch1 <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token constant">B</span> <span class="token operator">=</span> <span class="token keyword">await</span> fetch2 <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token constant">C</span> <span class="token operator">=</span> <span class="token keyword">await</span> fetch3 <span class="token punctuation">;</span> <span class="token keyword">const</span> result <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token constant">A</span> <span class="token punctuation">,</span> <span class="token constant">B</span> <span class="token punctuation">,</span> <span class="token constant">C</span> <span class="token punctuation">,</span> time <span class="token operator">:</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">Date</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getTime</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> startTime <span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">1000</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">"runBetterFetch :>> "</span> <span class="token punctuation">,</span> result <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token function">runFetch</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">runBetterFetch</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
The result will be:
1 2 3 | runBetterFetch :>> { A: 'OK', B: 'OK', C: 'OK', time: 3.005 } runFetch :>> { A: 'OK', B: 'OK', C: 'OK', time: 6.03 } |
Promise.all
You can also use Promise.all
to solve the above problem (or Promise.allSettled
in case you don’t care about failed request
).
1 2 3 4 5 6 | <span class="token keyword">const</span> <span class="token punctuation">[</span> banners <span class="token punctuation">,</span> events <span class="token punctuation">,</span> notices <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">await</span> Promise <span class="token punctuation">.</span> <span class="token function">all</span> <span class="token punctuation">(</span> <span class="token punctuation">[</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">"/banners"</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">"/events"</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token function">fetch</span> <span class="token punctuation">(</span> <span class="token string">"/notices"</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> |
Conclusion
Using async
/ await
syntax makes our lives easier and happier, but we should use them well. Simply putting the await
keyword somewhere can have very different consequences depending on where you put it.
Roundup
As always, I hope you enjoyed this article and learned something new.
Thank you and see you in the next posts!
If you find this blog interesting, please give me a like and subscribe to support me. Thank you.