1<!DOCTYPE html><html class="dark"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><title>Understanding JavaScript Asynchronous Behavior</title><meta name="robots" content="index,follow"/><meta name="description" content="A deep dive into how JavaScript handles asynchronous operations, from callbacks to Promises to async/await."/><meta property="og:title" content="Understanding JavaScript Asynchronous Behavior"/><meta property="og:description" content="A deep dive into how JavaScript handles asynchronous operations, from callbacks to Promises to async/await."/><meta property="og:url" content="https://brian.dev/articles/forecast-cloudy"/><meta property="og:image" content="https://og.brian.dev/api/og?title=Understanding JavaScript Asynchronous Behavior&desc=A deep dive into how JavaScript handles asynchronous operations, from callbacks to Promises to async/await."/><meta property="og:image:alt" content="Og Image Alt"/><meta property="og:image:type" content="image/jpeg"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="600"/><meta property="og:site_name" content="brian.dev"/><link rel="canonical" href="https://brian.dev/articles/forecast-cloudy"/><meta name="next-head-count" content="15"/>
1<script> 2 let darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)') 3 4 updateMode() 5 darkModeMediaQuery.addEventListener('change', updateModeWithoutTransitions) 6 window.addEventListener('storage', updateModeWithoutTransitions) 7 8 function updateMode() { 9 let isSystemDarkMode = darkModeMediaQuery.matches 10 let isDarkMode = window.localStorage.isDarkMode === 'true' || (!('isDarkMode' in window.localStorage) && isSystemDarkMode) 11 12 if (isDarkMode) { 13 document.documentElement.classList.add('dark') 14 } else { 15 document.documentElement.classList.remove('dark') 16 } 17 18 if (isDarkMode === isSystemDarkMode) { 19 delete window.localStorage.isDarkMode 20 } 21 } 22 23 function disableTransitionsTemporarily() { 24 document.documentElement.classList.add('[&_*]:!transition-none') 25 window.setTimeout(() => { 26 document.documentElement.classList.remove('[&_*]:!transition-none') 27 }, 0) 28 } 29 30 function updateModeWithoutTransitions() { 31 disableTransitionsTemporarily() 32 updateMode() 33 } 34</script>
34<link rel="alternate" type="application/rss+xml" href="undefined/rss/feed.xml"/><link rel="alternate" type="application/feed+json" href="undefined/rss/feed.json"/><link rel="me" href="https://tty0.social/@bketelsen"/><link rel="preload" href="/_next/static/css/56c2344ec8148103.css" as="style"/><link rel="stylesheet" href="/_next/static/css/56c2344ec8148103.css" data-n-g=""/><noscript data-n-css=""></noscript>
34<script defer="" nomodule="" src="/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js"></script>
34<script src="/_next/static/chunks/webpack-c4acd79e87956a0e.js" defer=""></script>
34<script src="/_next/static/chunks/framework-7751730b10fa0f74.js" defer=""></script>
34<script src="/_next/static/chunks/main-bf11fce9f4e3622f.js" defer=""></script>
34<script src="/_next/static/chunks/pages/_app-0fac12d0b622de3f.js" defer=""></script>
34<script src="/_next/static/chunks/970-20c4bbbfd2bfc04a.js" defer=""></script>
34<script src="/_next/static/chunks/pages/articles/forecast-cloudy-3682464fc0732b5c.js" defer=""></script>
34<script src="/_next/static/uvWpdkUNmi8lIm869yVZm/_buildManifest.js" defer=""></script>
34<script src="/_next/static/uvWpdkUNmi8lIm869yVZm/_ssgManifest.js" defer=""></script>
34</head><body class="dark:bg-black bg-white">
34<script> 35 (function () { 36 document.documentElement.classList.add('dark'); 37 localStorage.setItem('theme', 'dark'); 38 })() 39 </script>
39<div id="__next"><div style="visibility:hidden"><div class="sm:px-8 mt-16 lg:mt-32"><div class="mx-auto max-w-7xl lg:px-8"><div class="relative px-4 sm:px-8 lg:px-12"><div class="mx-auto max-w-2xl lg:max-w-5xl"><div class="xl:relative"><div class="mx-auto max-w-2xl"><article><header class="flex flex-col"><h1 class="mt-6 text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">Understanding JavaScript Asynchronous Behavior</h1><time dateTime="2024-06-17" class="order-first flex items-center text-base text-zinc-500 dark:text-zinc-200"><span class="h-4 w-0.5 rounded-full bg-zinc-500 dark:bg-zinc-200"></span><span class="ml-3">June 17, 2024</span></time></header><div class="mt-8 pt-6 prose prose-lg text-xl max-w-none dark:prose-invert dark:prose-hr:border-zinc-800"><p>JavaScript is single-threaded, but it can handle asynchronous operations efficiently. Let's explore how this magic happens.</p><h2>The Event Loop: JavaScript's Heart</h2><p>At its core, JavaScript uses an event loop to handle asynchronous operations. Think of it as a traffic controller that manages the flow of code execution.</p><h2>Example.js</h2><pre><code class="code-highlight"><span class="code-line">console.log('First') 40</span><span class="code-line">setTimeout(() => console.log('Second'), 0) 41</span><span class="code-line">console.log('Third') 42</span><span class="code-line">// Output: 43</span><span class="code-line">// First 44</span><span class="code-line">// Third 45</span><span class="code-line">// Second 46</span></code></pre><p>Even with a 0ms timeout, 'Second' prints last. Why? Because <code>setTimeout</code> is asynchronous!</p><h2>Callbacks: The Traditional Approach</h2><p>Callbacks were the original way to handle asynchronous operations:</p><h2>Example.js</h2><pre><code class="code-highlight"><span class="code-line">function fetchData(callback) { 47</span><span class="code-line">setTimeout(() => { 48</span><span class="code-line">const data = { id: 1, name: 'Ankit' } 49</span><span class="code-line">callback(data) 50</span><span class="code-line">}, 1000) 51</span><span class="code-line">} 52</span><span class="code-line">fetchData((data) => { 53</span><span class="code-line">console.log(data) // Runs after 1 second 54</span><span class="code-line">}) 55</span><span class="code-line"> 56</span></code></pre><div class="not-prose my-8 flex rounded-3xl p-6 bg-amber-50 dark:bg-slate-800/60 dark:ring-1 dark:ring-slate-300/10"><svg aria-hidden="true" viewBox="0 0 32 32" fill="none" class="flex-none w-8 h-8 [--icon-foreground:theme(colors.amber.900)] [--icon-background:theme(colors.amber.100)]"><defs><radialGradient cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" id=":R2bl6:-gradient" gradientTransform="rotate(65.924 1.519 20.92) scale(25.7391)"><stop stop-color="#FDE68A" offset=".08"></stop><stop stop-color="#F59E0B" offset=".837"></stop></radialGradient><radialGradient cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" id=":R2bl6:-gradient-dark" gradientTransform="matrix(0 24.5 -24.5 0 16 5.5)"><stop stop-color="#FDE68A" offset=".08"></stop><stop stop-color="#F59E0B" offset=".837"></stop></radialGradient></defs><g class="dark:hidden"><circle cx="20" cy="20" r="12" fill="url(#:R2bl6:-gradient)"></circle><path d="M3 16c0 7.18 5.82 13 13 13s13-5.82 13-13S23.18 3 16 3 3 8.82 3 16Z" fill-opacity="0.5" class="fill-[var(--icon-background)] stroke-[color:var(--icon-foreground)]" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="m15.408 16.509-1.04-5.543a1.66 1.66 0 1 1 3.263 0l-1.039 5.543a.602.602 0 0 1-1.184 0Z" class="fill-[var(--icon-foreground)] stroke-[color:var(--icon-foreground)]" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M16 23a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" fill-opacity="0.5" stroke="currentColor" class="fill-[var(--icon-background)] stroke-[color:var(--icon-foreground)]" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g><g class="hidden dark:inline"><path fill-rule="evenodd" clip-rule="evenodd" d="M2 16C2 8.268 8.268 2 16 2s14 6.268 14 14-6.268 14-14 14S2 23.732 2 16Zm11.386-4.85a2.66 2.66 0 1 1 5.228 0l
56-1.039 5.543a1.602 1.602 0 0 1-3.15 0l-1.04-5.543ZM16 20a2 2 0 1 0 0 4 2 2 0 0 0 0-4Z" fill="url(#:R2bl6:-gradient-dark)"></path></g></svg><div class="flex-auto ml-4"><p class="m-0 font-display text-xl text-amber-900 dark:text-amber-500">Callback Hell</p><div class="prose mt-2.5 text-amber-800 prose-code:text-amber-900 prose-a:text-amber-900 [--tw-prose-underline:theme(colors.amber.400)] dark:[--tw-prose-underline:theme(colors.sky.700)] [--tw-prose-background:theme(colors.amber.50)] dark:text-slate-300 dark:prose-code:text-slate-300"><p>Nested callbacks can lead to difficult-to-maintain code, often called "callback hell":</p><div class="remark-code-title">callback-hell.js</div><pre class="language-javascript"><code class="code-highlight language-javascript"><span class="code-line line-number" line="1"><span class="token function">fetchUser</span><span class="token punctuation">(</span><span class="token parameter">user</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> 57</span><span class="code-line line-number" line="2"> <span class="token function">fetchProfile</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span><span class="token property-access">id</span><span class="token punctuation">,</span> <span class="token parameter">profile</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> 58</span><span class="code-line line-number" line="3"> <span class="token function">fetchPosts</span><span class="token punctuation">(</span>profile<span class="token punctuation">.</span><span class="token property-access">id</span><span class="token punctuation">,</span> <span class="token parameter">posts</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> 59</span><span class="code-line line-number" line="4"> <span class="token comment">// Deep nesting continues...</span> 60</span><span class="code-line line-number" line="5"> <span class="token punctuation">}</span><span class="token punctuation">)</span> 61</span><span class="code-line line-number" line="6"> <span class="token punctuation">}</span><span class="token punctuation">)</span> 62</span><span class="code-line line-number" line="7"><span class="token punctuation">}</span><span class="token punctuation">)</span> 63</span></code></pre></div></div></div><h2>Promises: A Better Way</h2><p>Promises provide a more elegant solution for handling asynchronous operations:</p><p><code>javascript:promise-example.js</code></p><pre><code class="code-highlight"><span class="code-line">function fetchUserData() { 64</span><span class="code-line">return new Promise((resolve, reject) => { 65</span><span class="code-line">setTimeout(() => { 66</span><span class="code-line">const user = { id: 1, name: 'Ankit' } 67</span><span class="code-line">resolve(user) 68</span><span class="code-line">// or if something goes wrong: 69</span><span class="code-line">// reject(new Error('Failed to fetch user')) 70</span><span class="code-line">}, 1000) 71</span><span class="code-line">}) 72</span><span class="code-line">} 73</span><span class="code-line">fetchUserData() 74</span><span class="code-line">.then(user => console.log(user)) 75</span><span class="code-line">.catch(error => console.error(error)) 76</span></code></pre><p>Promises can be chained, making the code more readable:</p><p><code>javascript:promise-chain.js</code> <code>showLineNumbers {2-4}</code></p><pre><code class="code-highlight"><span class="code-line">fetchUser() 77</span><span class="code-line">.then(user => fetchProfile(user.id)) 78</span><span class="code-line">.then(profile => fetchPosts(profile.id)) 79</span><span class="code-line">.then(posts => console.log(posts)) 80</span><span class="code-line">.catch(error => console.error(error)) 81</span></code></pre><h2>Async/Await: The Modern Approach</h2><p>Async/await makes asynchronous code look and behave more like synchronous code:</p><p><code>javascript</code> <code>:async-await.js</code> <code>showLineNumbers</code></p><pre><code class="code-highlight"><span class="code-line">async function getUserData() { 82</span><span class="code-line">try { 83</span><span class="code-line">const user = await fetchUser() 84</span><span class="code-line">const profile = await fetchProfile(user.id) 85</span><span class="code-line">const posts = await fetchPosts(profile.id) 86</span><span class="code-line">return posts 87</span><span class="code-line">} catch (error) { 88</span><span class="code-line">console.error('Error fetching user data:', error) 89</span><span class="code-line">} 90</span><span class="code-line">} 91</span></code></pre><h2>Real-World Example: Fetching API Data</h2><p>Here's a practical example combining these concepts:</p><p><code>javascript</code> <code>github-api.js</code></p><pre><code class="code-highlight"><span class="code-line">async function fetchGitHubUser(username) { 92</span><span class="code-line">try { 93</span><span class="code-line">const response = await fetch(https://api.github.com/users/${username}) 94</span><span class="code-line">if (!response.ok) { 95</span><span class="code-line">throw new Error('User not found') 96</span><span class="code-line">} 97</span><span class="code-line">const userData = await response.json() 98</span><span class="code-line">return userData 99</span><span class="code-line">} catch (error) { 100</span><span class="code-line">console.error('Error:', error.message) 101</span><span class="code-line">throw error 102</span><span class="code-line">} 103</span><span class="code-line">} 104</span><span class="code-line">// Using the function 105</span><span class="code-line">fetchGitHubUser('Ankitmohanty2') 106</span><span class="code-line">.then(user =>
106 console.log(user)) 107</span><span class="code-line">.catch(error => console.error(error)) 108</span></code></pre><h2>Best Practices</h2><ol><li><strong>Always handle errors</strong>: Use try/catch with async/await or .catch() with Promises.</li><li><strong>Avoid mixing patterns</strong>: Stick to either Promises or async/await in a project.</li><li><strong>Use Promise.all() for parallel operations</strong>:</li></ol><p><code>javascript</code> <code>parallel-operations.js</code></p><pre><code class="code-highlight"><span class="code-line">const [users, posts, comments] = await Promise.all([ 109</span><span class="code-line">fetchUsers(), 110</span><span class="code-line">fetchPosts(), 111</span><span class="code-line">fetchComments() 112</span><span class="code-line">]) 113</span><span class="code-line"> 114</span></code></pre><h2>Common Pitfalls to Avoid</h2><ol><li><strong>Not handling rejections</strong>: Always catch potential errors in your async operations.</li><li><strong>Blocking the main thread</strong>: Avoid long-running synchronous operations.</li><li><strong>Sequential vs Parallel</strong>: Know when to use <code>await</code> in sequence vs <code>Promise.all()</code> for parallel execution.</li></ol><p><code>javascript</code> <code>sequential-vs-parallel.js</code></p><pre><code class="code-highlight"><span class="code-line">// Sequential (slower) 115</span><span class="code-line">const userData = await fetchUser() 116</span><span class="code-line">const postData = await fetchPosts() 117</span><span class="code-line">// Parallel (faster) 118</span><span class="code-line">const [userData, postData] = await Promise.all([ 119</span><span class="code-line">fetchUser(), 120</span><span class="code-line">fetchPosts() 121</span><span class="code-line">]) 122</span><span class="code-line"> 123</span></code></pre><h2>Advanced Patterns</h2><h3>Race Conditions</h3><p>Sometimes you need to handle multiple promises but only care about the first one to complete:</p><p><code>javascript</code> <code>race-condition.js</code></p><pre><code class="code-highlight"><span class="code-line">const result = await Promise.race([ 124</span><span class="code-line">fetch('api1.example.com'), 125</span><span class="code-line">fetch('api2.example.com') 126</span><span class="code-line">]) 127</span><span class="code-line"> 128</span></code></pre><h3>Error Boundaries</h3><p>Creating reusable error handling patterns:</p><p><code>javascript</code> <code>error-boundary.js</code></p><pre><code class="code-highlight"><span class="code-line">async function withErrorHandling(asyncFn) { 129</span><span class="code-line">try { 130</span><span class="code-line">return await asyncFn() 131</span><span class="code-line">} catch (error) { 132</span><span class="code-line">console.error('Operation failed:', error) 133</span><span class="code-line">// Custom error handling logic 134</span><span class="code-line">throw error 135</span><span class="code-line">} 136</span><span class="code-line">} 137</span><span class="code-line">// Usage 138</span><span class="code-line">const data = await withErrorHandling(async () => { 139</span><span class="code-line">const response = await fetch('/api/data') 140</span><span class="code-line">return response.json() 141</span><span class="code-line">}) 142</span><span class="code-line"> 143</span></code></pre><h2>Conclusion</h2><p>Understanding asynchronous JavaScript is crucial for modern web development. While callbacks served us well, Promises and async/await provide cleaner, more maintainable solutions for handling asynchronous operations.</p><p>Remember: JavaScript's asynchronous nature isn't about doing multiple things at exactly the same time (it's still single-threaded), but about handling operations efficiently without blocking the main thread.</p><h2>References</h2><ul><li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous">MDN Web Docs - Asynchronous JavaScript</a></li><li><a href="https://javascript.info/async">JavaScript.info - Promises, async/await</a></li><li><a href="https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick">Node.js Event Loop Documentation</a></li></ul></div></article></div></div></div></div></div></div></div></div>
143<script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/articles/forecast-cloudy","query":{},"buildId":"uvWpdkUNmi8lIm869yVZm","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script>
143<div style="display:none"> <a rel="me" href="https://tty0.social/@bketelsen">Mastodon</a></div></body></html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.