{"id":"l-js-4-5","courseId":"javascript-fundamentals","lang":"en","kind":"lesson","summary":"Explain why JavaScript waits without freezing, read and write promise-based code with then and async/await, call fetch with proper error handling, and predict output order using the event loop.","html":"<p>Pocket would be more useful if it could show your spending in euros too, which means asking a server for today’s exchange rates. That request might take 50 milliseconds or five seconds. If JavaScript simply stopped and waited, the whole page would freeze: no scrolling, no typing, no clicks. Since your code shares one thread with the page itself, it needs a way to start slow work, carry on, and pick up the result later. That is what <strong>asynchronous</strong> code is.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Callbacks: “call me when it is done”</h2>\n<p>The oldest form of “later” is a callback. <code dir=\"ltr\">setTimeout</code> asks the browser to call a function after a delay:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"0\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Requesting rates…\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">setTimeout</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Rates arrived\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">100</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Still responsive\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre><div class=\"run-output\" data-run-output=\"0\" hidden></div></div>\n<p>“Still responsive” prints before “Rates arrived”. <code dir=\"ltr\">setTimeout</code> does not pause anything; it hands the function to the browser and returns immediately. Callbacks work, but when step two needs step one’s result and step three needs step two’s, they nest deeper and deeper, and error handling has to be repeated at every level. Promises were added to fix that.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Promises: a value that arrives later</h2>\n<p>A <strong>promise</strong> is an object that stands for a result that is not ready yet. It starts <strong>pending</strong>, and settles exactly once: <strong>fulfilled</strong> with a value, or <strong>rejected</strong> with an error. You attach what should happen next with <code dir=\"ltr\">.then</code> (on success) and <code dir=\"ltr\">.catch</code> (on failure).</p>\n<p>This course’s sandbox has no network, so the examples use <code dir=\"ltr\">fakeFetch</code>, a stand-in that behaves like the real <code dir=\"ltr\">fetch</code>: it returns a promise of a response object with <code dir=\"ltr\">ok</code>, <code dir=\"ltr\">status</code> and a <code dir=\"ltr\">json()</code> method.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"1\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> fakeFetch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">url</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> new</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> Promise</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">resolve</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">    setTimeout</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">      if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (url </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"/api/rates\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">        resolve</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ ok: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">true</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, status: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">200</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">json</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> () </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> ({ EUR: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.86</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, GBP: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.75</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }) });</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">else</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">        resolve</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ ok: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">false</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, status: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">404</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">json</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> () </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> ({ error: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Not found\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }) });</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    }, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">50</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  });</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">fakeFetch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/api/rates\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  .</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">then</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">response</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> response.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">json</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">())</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  .</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">then</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">rates</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"EUR rate:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, rates.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">EUR</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  .</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">catch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">error</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Failed:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, error.message));</span></span></code></pre><div class=\"run-output\" data-run-output=\"1\" hidden></div></div>\n<p>Each <code dir=\"ltr\">.then</code> returns a new promise, so steps chain in a flat line instead of nesting, and one <code dir=\"ltr\">.catch</code> at the end handles a failure from any step. You will rarely create promises with <code dir=\"ltr\">new Promise</code> yourself; libraries and browser APIs hand them to you, and your job is to consume them.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">async and await: promises that read like normal code</h2>\n<p><code dir=\"ltr\">async</code>/<code dir=\"ltr\">await</code> is a more readable way to write the same thing. Inside a function marked <code dir=\"ltr\">async</code>, <code dir=\"ltr\">await promise</code> pauses <strong>that function</strong> until the promise settles, then gives you its value. Errors become ordinary exceptions you catch with <code dir=\"ltr\">try</code>/<code dir=\"ltr\">catch</code>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"2\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> fakeFetch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">url</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> new</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> Promise</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">resolve</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">    setTimeout</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">      const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> ok</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> url </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"/api/rates\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">      const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> body</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> ok </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">?</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { EUR: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.86</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { error: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Not found\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> };</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">      resolve</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ ok, status: ok </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">?</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 200</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> :</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 404</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">json</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> () </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> body });</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    }, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">50</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  });</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> getJson</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">url</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> response</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> await</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> fakeFetch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(url);</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">!</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">response.ok) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    throw</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> new</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> Error</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">`HTTP ${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">response</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">status</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">} for ${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">url</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> response.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">json</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> showRates</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  try</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> rates</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> await</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> getJson</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/api/rates\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"EUR:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, rates.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">EUR</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    await</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> getJson</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/api/missing\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">catch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (error) {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Could not load:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, error.message);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">showRates</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span></code></pre><div class=\"run-output\" data-run-output=\"2\" hidden></div></div>\n<p>Notice what <code dir=\"ltr\">await</code> does <strong>not</strong> do: it does not freeze the page. While <code dir=\"ltr\">showRates</code> waits, the browser keeps handling clicks and drawing frames. Only this one function is paused.</p>\n<p>An <code dir=\"ltr\">async</code> function always returns a promise, even when you <code dir=\"ltr\">return</code> a plain value. So whoever calls it must <code dir=\"ltr\">await</code> it too, or use <code dir=\"ltr\">.then</code>.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Forgetting await</span></p>\n<p><code dir=\"ltr\">const rates = getJson(&quot;/api/rates&quot;); console.log(rates.EUR);</code> logs <code dir=\"ltr\">undefined</code>, because <code dir=\"ltr\">rates</code> is a pending promise, not the data. Logging <code dir=\"ltr\">rates</code> itself shows <code dir=\"ltr\">Promise { &lt;pending&gt; }</code>, which is the giveaway. Add <code dir=\"ltr\">await</code> (inside an <code dir=\"ltr\">async</code> function).</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">fetch, for real</h2>\n<p>In a real page, you replace <code dir=\"ltr\">fakeFetch</code> with the browser’s <code dir=\"ltr\">fetch</code>, and the shape of the code stays the same:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">rates.js</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> getJson</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">url</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> response</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> await</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> fetch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(url);</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">!</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">response.ok) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    throw</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> new</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> Error</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">`HTTP ${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">response</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">status</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">} for ${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">url</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> response.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">json</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>The <code dir=\"ltr\">response.ok</code> check is essential, and many tutorials leave it out. <code dir=\"ltr\">fetch</code> only rejects when the request could not happen at all, for example when the user is offline. A 404 or a 500 is a perfectly good HTTP response, so the promise <strong>resolves</strong>, with <code dir=\"ltr\">ok</code> set to <code dir=\"ltr\">false</code>. Without the check, your code would try to read rates out of an error page.</p>\n<p>When two requests do not depend on each other, start both and wait once with <code dir=\"ltr\">Promise.all</code> (inside an <code dir=\"ltr\">async</code> function, like every <code dir=\"ltr\">await</code>), which fulfils with an array of results or rejects as soon as either fails:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">rates.js</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">rates</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">categories</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> await</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> Promise</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">all</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">([</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  getJson</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/api/rates\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">),</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  getJson</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/api/categories\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">),</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]);</span></span></code></pre></div>\n<h2 id=\"sec-5\" tabindex=\"-1\">The event loop</h2>\n<p>How can one thread do all this? The engine runs your code on a <strong>call stack</strong>, one function at a time. Slow things, such as timers and network requests, are handled by the browser outside the stack. When one finishes, its callback is put in a queue. Whenever the stack is empty, the <strong>event loop</strong> takes the next callback from a queue and runs it.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 260\" role=\"img\" aria-labelledby=\"l-js-4-5-t1\"><defs><marker id=\"arrow-l-js-4-5-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-js-4-5-t1\">Your code runs on the call stack. Timers and fetch are handled by the browser, which puts finished callbacks into queues. Promise callbacks go to the microtask queue, timers and events to the task queue. When the stack is empty, the event loop first runs all microtasks, then one task.</title>\n  <rect class=\"d-box-primary\" x=\"20\" y=\"30\" width=\"170\" height=\"170\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"105\" y=\"56\" text-anchor=\"middle\">Call stack</text>\n  <rect class=\"d-box\" x=\"40\" y=\"74\" width=\"130\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"105\" y=\"96\" text-anchor=\"middle\">showRates()</text>\n  <rect class=\"d-box\" x=\"40\" y=\"116\" width=\"130\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"105\" y=\"138\" text-anchor=\"middle\">main script</text>\n  <rect class=\"d-box-accent\" x=\"250\" y=\"30\" width=\"170\" height=\"80\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"335\" y=\"58\" text-anchor=\"middle\">Browser</text>\n  <text class=\"d-label\" x=\"335\" y=\"84\" text-anchor=\"middle\">timers, fetch</text>\n  <rect class=\"d-box-success\" x=\"480\" y=\"30\" width=\"180\" height=\"70\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"570\" y=\"56\" text-anchor=\"middle\">Microtasks</text>\n  <text class=\"d-label\" x=\"570\" y=\"80\" text-anchor=\"middle\">promise callbacks</text>\n  <rect class=\"d-box-warn\" x=\"480\" y=\"140\" width=\"180\" height=\"70\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"570\" y=\"166\" text-anchor=\"middle\">Tasks</text>\n  <text class=\"d-label\" x=\"570\" y=\"190\" text-anchor=\"middle\">timers, clicks</text>\n  <path class=\"d-arrow\" d=\"M190 70 L246 70\" marker-end=\"url(#arrow-l-js-4-5-1)\"/>\n  <path class=\"d-arrow\" d=\"M420 60 L476 60\" marker-end=\"url(#arrow-l-js-4-5-1)\"/>\n  <path class=\"d-arrow\" d=\"M420 90 L476 160\" marker-end=\"url(#arrow-l-js-4-5-1)\"/>\n  <path class=\"d-arrow\" d=\"M480 90 Q330 150 194 150\" marker-end=\"url(#arrow-l-js-4-5-1)\"/>\n  <path class=\"d-arrow\" d=\"M480 190 Q330 235 194 185\" marker-end=\"url(#arrow-l-js-4-5-1)\"/>\n  <text class=\"d-label-muted\" x=\"335\" y=\"142\" text-anchor=\"middle\">1. all microtasks</text>\n  <text class=\"d-label-muted\" x=\"335\" y=\"240\" text-anchor=\"middle\">2. then one task</text>\n</svg><figcaption>The event loop runs queued callbacks when the stack is empty</figcaption></figure>\n<p>There are two queues, and the order between them explains many puzzles. Promise callbacks go into the <strong>microtask queue</strong>; timers and events go into the <strong>task queue</strong>. Each time the stack empties, the loop runs <strong>every</strong> waiting microtask first, then one task, then checks microtasks again:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"5\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"5\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"1: script\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">setTimeout</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"4: timeout task\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">), </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">Promise</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">resolve</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">().</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">then</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"3: promise microtask\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">));</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"2: script\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre><div class=\"run-output\" data-run-output=\"5\" hidden></div></div>\n<p>A timeout of 0 ms does not mean “now”; it means “as a task, once the stack is clear and the microtasks are done”. The same model explains why a long loop freezes the page: while your code occupies the stack, no click handler, timer or redraw can run.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Show loading and error states</span></p>\n<p>Every network call has three outcomes the user can see: waiting, success and failure. Set a “Loading…” message before the <code dir=\"ltr\">await</code>, replace it with the data on success, and show a helpful message in the <code dir=\"ltr\">catch</code>. An app that silently shows nothing while it waits, or after it fails, feels broken.</p>\n</aside>\n<p>Section 4 is complete: Pocket lives on a page, reacts to clicks, takes input, saves data and can talk to a server. Section 5 turns this growing pile of code into a well-organised program.</p>\n","toc":[{"id":"sec-1","text":"Callbacks: “call me when it is done”","level":2},{"id":"sec-2","text":"Promises: a value that arrives later","level":2},{"id":"sec-3","text":"async and await: promises that read like normal code","level":2},{"id":"sec-4","text":"fetch, for real","level":2},{"id":"sec-5","text":"The event loop","level":2}],"takeaways":["JavaScript runs one thing at a time; slow work such as network requests and timers happens outside your code, which is called back when it finishes.","A promise is an object standing for a value that will arrive later; it ends up fulfilled with a value or rejected with an error.","<code dir=\"ltr\">await</code> pauses only the surrounding <code dir=\"ltr\">async</code> function until a promise settles; wrap awaits in <code dir=\"ltr\">try</code>/<code dir=\"ltr\">catch</code> to handle rejections.","<code dir=\"ltr\">fetch</code> rejects only when the network fails; for HTTP errors such as 404 it resolves with <code dir=\"ltr\">response.ok</code> set to false, so always check it.","After each piece of synchronous code, the event loop runs all queued promise callbacks before the next timer or event."],"further":[{"title":"Using promises (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises"},{"title":"async function (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function"},{"title":"Using the Fetch API (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch"},{"title":"The event loop (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Execution_model"}],"quiz":[{"q":"<p>In what order are the lines logged?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"A\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">setTimeout</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"B\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">), </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">Promise</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">resolve</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">().</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">then</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"C\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">));</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"D\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre></div>\n","options":[{"text":"A, B, C, D","why":"That is the order the lines are written, but B and C are callbacks that run later, after all synchronous code."},{"text":"A, D, B, C","why":"Both callbacks wait for the synchronous code, but promise callbacks (microtasks) run before timer callbacks."},{"text":"A, D, C, B","why":"Correct. A and D run now. Then the microtask queue empties ©, and only then does the timer’s task run (B), even with a 0 ms delay."}],"answer":2},{"q":"<p><code dir=\"ltr\">const data = getRates(); console.log(data.EUR);</code> where <code dir=\"ltr\">getRates</code> is an <code dir=\"ltr\">async</code> function. What is logged?</p>","options":[{"text":"The EUR rate, because <code dir=\"ltr\">async</code> functions return their value directly.","why":"An <code dir=\"ltr\">async</code> function always returns a promise. Without <code dir=\"ltr\">await</code>, <code dir=\"ltr\">data</code> is that promise, not the rates."},{"text":"<code dir=\"ltr\">undefined</code>, because <code dir=\"ltr\">data</code> is a pending promise, which has no <code dir=\"ltr\">EUR</code> property.","why":"Correct. Write <code dir=\"ltr\">const data = await getRates();</code> inside an async function, or use <code dir=\"ltr\">.then</code>."},{"text":"It throws, because you cannot call an async function without <code dir=\"ltr\">await</code>.","why":"You can call it; you just get a promise back. Forgetting <code dir=\"ltr\">await</code> fails quietly, which is why it is a common bug."}],"answer":1},{"q":"<p>Your server returns 404 Not Found for <code dir=\"ltr\">fetch(&quot;/api/rates&quot;)</code>. What does the fetch promise do?</p>","options":[{"text":"It resolves with a response whose <code dir=\"ltr\">ok</code> is false and <code dir=\"ltr\">status</code> is 404.","why":"Correct. HTTP errors are still successful network round trips. You must check <code dir=\"ltr\">response.ok</code> yourself and throw if you want a rejection."},{"text":"It rejects, so your <code dir=\"ltr\">catch</code> block runs.","why":"<code dir=\"ltr\">fetch</code> only rejects for network-level failures such as being offline. A 404 is a valid HTTP response."},{"text":"It never settles, so your code waits forever.","why":"The server answered, so the promise settles. It resolves with the 404 response."}],"answer":0},{"q":"<p>You need the rates and the category list, from two independent URLs, before rendering. Which is fastest?</p>","options":[{"text":"<code dir=\"ltr\">const r = await getRates(); const c = await getCategories();</code>","why":"This works, but the second request only starts after the first finishes, so you wait for both one after the other."},{"text":"<code dir=\"ltr\">const [r, c] = await Promise.all([getRates(), getCategories()]);</code>","why":"Correct. Both requests start immediately and you wait once, for the slower of the two."},{"text":"<code dir=\"ltr\">const r = getRates(); const c = getCategories();</code> without await.","why":"Both start in parallel, but <code dir=\"ltr\">r</code> and <code dir=\"ltr\">c</code> are promises, not data, so rendering with them would fail."}],"answer":1}],"exercise":{"kind":"web","mode":"js","starter":{"js":"// A stand-in for fetch: the sandbox has no network. It returns a promise\n// of a response with ok, status and json(), after a short delay.\nfunction fakeFetch(url) {\n  const routes = {\n    \"/api/rates\": { EUR: 0.86, GBP: 0.75 },\n    \"/api/categories\": [{ name: \"food\" }, { name: \"travel\" }, { name: \"fun\" }],\n  };\n  return new Promise((resolve) => {\n    setTimeout(() => {\n      const ok = url in routes;\n      const body = ok ? routes[url] : { error: \"Not found\" };\n      resolve({ ok, status: ok ? 200 : 404, json: async () => body });\n    }, 30);\n  });\n}\n\n// Return the parsed JSON body, or throw an Error mentioning the status\nasync function getJson(url) {\n  const response = fakeFetch(url);\n  return response.json();\n}\n\n// [\"food\", \"travel\", \"fun\"], from /api/categories\nasync function loadCategoryNames() {\n  const categories = getJson(\"/api/categories\");\n  return categories.map((c) => c.name);\n}\n\n// The amount converted to euro cents (rounded), using /api/rates\nasync function toEuroCents(cents) {\n}\n\n// The JSON from url, or `fallback` if anything goes wrong\nasync function safeGetJson(url, fallback) {\n  return getJson(url);\n}\n"},"solution":{"js":"// A stand-in for fetch: the sandbox has no network. It returns a promise\n// of a response with ok, status and json(), after a short delay.\nfunction fakeFetch(url) {\n  const routes = {\n    \"/api/rates\": { EUR: 0.86, GBP: 0.75 },\n    \"/api/categories\": [{ name: \"food\" }, { name: \"travel\" }, { name: \"fun\" }],\n  };\n  return new Promise((resolve) => {\n    setTimeout(() => {\n      const ok = url in routes;\n      const body = ok ? routes[url] : { error: \"Not found\" };\n      resolve({ ok, status: ok ? 200 : 404, json: async () => body });\n    }, 30);\n  });\n}\n\nasync function getJson(url) {\n  const response = await fakeFetch(url);\n  if (!response.ok) {\n    throw new Error(`HTTP ${response.status} for ${url}`);\n  }\n  return response.json();\n}\n\nasync function loadCategoryNames() {\n  const categories = await getJson(\"/api/categories\");\n  return categories.map((c) => c.name);\n}\n\nasync function toEuroCents(cents) {\n  const rates = await getJson(\"/api/rates\");\n  return Math.round(cents * rates.EUR);\n}\n\nasync function safeGetJson(url, fallback) {\n  try {\n    return await getJson(url);\n  } catch {\n    return fallback;\n  }\n}\n\nloadCategoryNames().then((names) => console.log(names));\n"},"checks":[{"id":"get-json","test":"const rates = await getJson('/api/rates');\nreturn rates && rates.EUR === 0.86;\n"},{"id":"get-json-error","test":"try { await getJson('/api/missing'); return false; } catch (e) { return e instanceof Error && e.message.includes('404'); }\n"},{"id":"names","test":"const names = await loadCategoryNames();\nreturn Array.isArray(names) && names.join() === 'food,travel,fun';\n"},{"id":"euros","test":"return (await toEuroCents(1000)) === 860 && (await toEuroCents(450)) === 387;\n"},{"id":"safe","test":"const fallback = [];\nconst bad = await safeGetJson('/api/nope', fallback);\nconst good = await safeGetJson('/api/rates', null);\nreturn bad === fallback && good && good.GBP === 0.75;\n"}],"title":"Load data asynchronously","prompt":"<p>The sandbox has no network, so the starter gives you <code dir=\"ltr\">fakeFetch(url)</code>, which behaves like <code dir=\"ltr\">fetch</code>: it returns a promise of a response with <code dir=\"ltr\">ok</code>, <code dir=\"ltr\">status</code> and an async <code dir=\"ltr\">json()</code> method. Write four <code dir=\"ltr\">async</code> functions:</p>\n<ul>\n<li><code dir=\"ltr\">getJson(url)</code> returns the parsed body, and <strong>throws</strong> an <code dir=\"ltr\">Error</code> whose message includes the status (such as <code dir=\"ltr\">404</code>) when <code dir=\"ltr\">response.ok</code> is false.</li>\n<li><code dir=\"ltr\">loadCategoryNames()</code> returns <code dir=\"ltr\">[&quot;food&quot;, &quot;travel&quot;, &quot;fun&quot;]</code>, the <code dir=\"ltr\">name</code> of each item from <code dir=\"ltr\">/api/categories</code>.</li>\n<li><code dir=\"ltr\">toEuroCents(cents)</code> returns the amount converted with the <code dir=\"ltr\">EUR</code> rate from <code dir=\"ltr\">/api/rates</code>, rounded to whole cents.</li>\n<li><code dir=\"ltr\">safeGetJson(url, fallback)</code> returns the JSON from <code dir=\"ltr\">url</code>, or <code dir=\"ltr\">fallback</code> if loading fails.</li>\n</ul>\n","hints":["The starter forgets <code dir=\"ltr\">await</code>: <code dir=\"ltr\">fakeFetch(url)</code> gives a promise, and a promise has no <code dir=\"ltr\">json</code> method. Write <code dir=\"ltr\">const response = await fakeFetch(url);</code>.","After awaiting, check <code dir=\"ltr\">if (!response.ok) { throw new Error(`HTTP ${response.status}`); }</code> before returning <code dir=\"ltr\">response.json()</code>.","<code dir=\"ltr\">safeGetJson</code> needs <code dir=\"ltr\">try { return await getJson(url); } catch { return fallback; }</code>. The <code dir=\"ltr\">await</code> inside the <code dir=\"ltr\">try</code> matters: without it, the rejection escapes the <code dir=\"ltr\">catch</code>."],"explanation":"<p>Every function that waits for something is <code dir=\"ltr\">async</code> and uses <code dir=\"ltr\">await</code>, so the code reads top to bottom even though each step finishes later. <code dir=\"ltr\">getJson</code> turns an HTTP error into a thrown <code dir=\"ltr\">Error</code>, because <code dir=\"ltr\">fetch</code> (and this fake) resolves successfully for a 404; after that, callers only need to handle one kind of failure. <code dir=\"ltr\">toEuroCents</code> rounds after converting, keeping Pocket’s whole-cents rule: 450 × 0.86 is 387.0, and 1000 × 0.86 is 860. In <code dir=\"ltr\">safeGetJson</code>, <code dir=\"ltr\">return await</code> inside <code dir=\"ltr\">try</code> is deliberate: awaiting there lets the rejection be thrown inside the <code dir=\"ltr\">try</code>, where the <code dir=\"ltr\">catch</code> can turn it into the fallback.</p>\n","checkLabels":{"get-json":"<code dir=\"ltr\">getJson</code> returns the parsed body","get-json-error":"<code dir=\"ltr\">getJson</code> throws an Error mentioning 404 for a missing URL","names":"<code dir=\"ltr\">loadCategoryNames</code> returns the category names","euros":"<code dir=\"ltr\">toEuroCents</code> converts with the EUR rate and rounds","safe":"<code dir=\"ltr\">safeGetJson</code> returns the fallback when loading fails"}},"words":846,"readMinutes":4}