Skip to main content

Session 9

Javascript Asynchronous Programming

JavaScript is inherently a single-threaded, synchronous language, meaning it executes code line-by-line from top to bottom. However, modern web applications frequently require tasks that take time to complete (e.g., fetching data from a server, waiting for user input, or running timers). If JavaScript waited for these tasks to finish before moving to the next line, the entire browser UI would freeze. This session introduces Asynchronous Programming, which allows JavaScript to initiate a long-running task, continue executing other code, and handle the result later using callbacks. This session covers the foundational async patterns required for responsive, non-blocking web applications.

Objectives

  • Understand the difference between synchronous and asynchronous execution
  • Master callback functions as the primary mechanism for async flow control
  • Implement built-in async methods: setTimeout() and setInterval()
  • Load external resources dynamically using XMLHttpRequest
  • Solve real-world async navigation and timing exercises

Synchronous vs. Asynchronous Execution

Feature Synchronous Asynchronous
Execution Flow Blocks until task completes Starts task, moves to next line immediately
UI Impact Freezes browser during long tasks Keeps UI responsive
Example console.log("Start"); console.log("End"); setTimeout(() => console.log("End"), 2000);

Callback Functions

A callback is a function passed as an argument to another function. It is not executed immediately; instead, it's "called back" at a later time when the parent function completes its task.

		// Function that accepts a callback
function fetchData(callback) {
  // Simulate delay
  setTimeout(() => {
    const data = "User Data Loaded";
    callback(data); // Execute callback when ready
  }, 1000);
}
 
// Pass callback WITHOUT parentheses
fetchData((result) => console.log(result)); // Logs after 1 second
	

Built-in Async Timer Methods

Method Syntax Behavior
setTimeout() setTimeout(callback, delayInMs) Executes callback once after delay
setInterval() setInterval(callback, intervalInMs) Executes callback repeatedly every interval
clearTimeout() / clearInterval() clearInterval(timerID) Stops the scheduled execution
		// Example: Live Clock
function updateClock() {
  const now = new Date();
  document.getElementById('clock').textContent = now.toLocaleTimeString();
}
const clockTimer = setInterval(updateClock, 1000); // Runs every second
clearInterval(clockTimer); // Stops it
	

Loading External Resources (XMLHttpRequest)

Before fetch() became standard, XMLHttpRequest (XHR) was the primary way to load external files asynchronously.

		const xhr = new XMLHttpRequest();
xhr.open("GET", "external.html", true); // true = async
xhr.onload = function() {
  if (xhr.status === 200) {
    document.getElementById('container').innerHTML = xhr.responseText;
  }
};
xhr.send(); // Triggers the request
	

Best Practices & Common Pitfalls

Pitfall Best Practice
Passing callback() with parentheses Pass function reference only: setTimeout(myFunc, 1000) not setTimeout(myFunc(), 1000)
Assuming code after setTimeout runs later setTimeout schedules execution; code immediately after it runs first
Forgetting to clear intervals Always store setInterval ID and use clearInterval() to prevent memory leaks
Nesting callbacks deeply ("Callback Hell") Keep nesting shallow; use named functions or modern Promise/async-await in production
Ignoring XHR status codes Always check xhr.status === 200 before processing responseText

Quick Guide

		// ⏱️ Timers
const timeoutID = setTimeout(() => console.log("Once"), 2000);
clearTimeout(timeoutID);
 
const intervalID = setInterval(() => console.log("Repeated"), 1000);
clearInterval(intervalID);
 
// 🔄 Callback Pattern
function asyncTask(param, callback) {
  // Do work...
  callback(result);
}
asyncTask("data", (res) => console.log(res));
 
// 🌐 XMLHttpRequest (Lab Requirement)
const xhr = new XMLHttpRequest();
xhr.open("GET", "file.html", true);
xhr.onload = () => { if(xhr.status===200) console.log(xhr.responseText); };
xhr.send();
	

Question 1

Problem Statement

Implement a clock in HTML which shows the live time in the format HH:MM:SS. Use the JavaScript setInterval() method and a callback function by displaying the system time every second.

Demo

Live Clock

05:19:39

Updates every second via setInterval()

Code

time.html
		<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Ex1: Live Clock</title>
        <style>
            body {
                font-family: sans-serif;
                text-align: center;
                padding: 50px;
                background: #f4f4f4;
            }
        </style>
    </head>
    <body>
        <h1>Live Clock</h1>
        <div
            id="clock"
            style="
                font-size: 4rem;
                font-weight: bold;
                font-family: monospace;
                margin-top: 20px;
            "
        >
            00:00:00
        </div>
 
        <script>
            // Callback function to update time
            function updateClock() {
                const now = new Date();
                const h = String(now.getHours()).padStart(2, "0");
                const m = String(now.getMinutes()).padStart(2, "0");
                const s = String(now.getSeconds()).padStart(2, "0");
                document.getElementById("clock").textContent = `${h}:${m}:${s}`;
            }
 
            // Set interval to execute callback every 1000ms
            setInterval(updateClock, 1000);
            updateClock(); // Initial call to avoid 1s delay
        </script>
    </body>
</html>
	

HTML View

Question 2

Problem Statement

Sometimes we use external resource files in our HTML page. The content of an external file cannot be used until loaded completely. This can be implemented with JavaScript Callback. Design a web page which loads an external HTML file on the event: onLoad. Hint: use the inbuilt class XMLHttpRequest() to use its functions: open(), onLoad() etc. to load an external HTML file.

Demo

External Content Loader (onMount)

Loading external.html...

Code

request.html
		<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Ex2: XHR Loader</title>
        <style>
            body {
                font-family: sans-serif;
                padding: 20px;
            }
            #output {
                border: 2px dashed #aaa;
                padding: 20px;
                margin-top: 15px;
                background: #fafafa;
                min-height: 50px;
            }
        </style>
    </head>
    <body>
        <h2>External Content Loader (onLoad Event)</h2>
        <div id="output">Loading external.html...</div>
 
        <script>
            window.addEventListener("load", () => {
                const xhr = new XMLHttpRequest();
                // true = asynchronous
                xhr.open("GET", "external.html", true);
 
                xhr.onload = function () {
                    if (xhr.status === 200) {
                        document.getElementById("output").innerHTML =
                            xhr.responseText;
                    } else {
                        document.getElementById("output").textContent =
                            `❌ Failed to load (Status: ${xhr.status})`;
                    }
                };
 
                xhr.onerror = () => {
                    document.getElementById("output").textContent =
                        "❌ Network error. Ensure server is running.";
                };
 
                xhr.send();
            });
        </script>
    </body>
</html>
	
external.html
		<h3 style="color: blue">External File Loaded Successfully!</h3>
	

HTML View

Question 3

Problem Statement

The village crows own an old scalpel that they occasionally use on special missions say, to cut through screen doors or packaging. To be able to quickly track it down, every time the scalpel is moved to another nest, an entry is added to the storage of both the nest that had it and the nest that took it, under the name "scalpel", with its new location as the value.This means that finding the scalpel is a matter of following the breadcrumb trail of storage entries, until you find a nest where that points at the nest itself.

Write an async function locateScalpel that does this, starting at the nest on which it runs. You can use the anyStorage function defined earlier to access storage in arbitrary nests. The scalpel has been going around long enough that you may assume that every nest has a "scalpel" entry in its data storage.

Demo

🦅 Scalpel Tracker

Code

scalpel.html
		<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Ex3: Locate Scalpel</title>
        <style>
            body {
                font-family: sans-serif;
                padding: 20px;
                max-width: 600px;
                margin: auto;
            }
            pre {
                background: #f4f4f4;
                padding: 15px;
                border-radius: 5px;
                white-space: pre-wrap;
            }
        </style>
    </head>
    <body>
        <h2>🦅 Scalpel Tracker</h2>
        <button id="findBtn">Find Scalpel</button>
        <pre id="output">Waiting...</pre>
 
        <script>
            // Mock nest data (simulating distributed storage)
            const nestData = {
                CrowNestA: { scalpel: "CrowNestB" },
                CrowNestB: { scalpel: "CrowNestC" },
                CrowNestC: { scalpel: "CrowNestC" }, // Points to itself → found!
            };
 
            // Simulated async anyStorage function
            function anyStorage(nest, name) {
                return new Promise((resolve) => {
                    setTimeout(
                        () => resolve(nestData[nest]?.[name] || null),
                        600,
                    );
                });
            }
 
            // Async locateScalpel implementation
            async function locateScalpel(startNest) {
                let current = startNest;
                let trail = [current];
 
                while (true) {
                    const next = await anyStorage(current, "scalpel");
                    if (next === current) break; // Found the self-referencing nest
                    trail.push(next);
                    current = next;
                }
                console.log({ foundAt: current, trail: trail.join(" → ") });
                return { foundAt: current, trail: trail.join(" → ") };
            }
 
            document
                .getElementById("findBtn")
                .addEventListener("click", async () => {
                    document.getElementById("output").textContent =
                        "🔍 Tracing scalpel breadcrumbs...";
                    const result = await locateScalpel("CrowNestA");
                    document.getElementById("output").textContent =
                        `Found at: ${result.foundAt}\n👣 Trail: ${result.trail}`;
                });
        </script>
    </body>
</html>
	

HTML View