Explain the difference between synchronous and asynchronous functions in JavaScript
TL;DR
Synchronous code runs to completion on the current call stack before later statements can run. Asynchronous APIs arrange for a result to be handled later through a callback, promise, or event, allowing the current stack to finish while the host waits for I/O or a timer. Asynchronous does not mean “runs on another thread”: an async function runs synchronously until its first suspension point, and CPU-heavy JavaScript still blocks its thread.
Asynchronous APIs commonly expose callbacks, promises, or events. Once the operation can make progress, its continuation is scheduled according to the host's event loop. This works especially well for I/O such as network and database requests; CPU-intensive work must instead be split up or moved to a worker to keep a browser UI responsive.
Execution over time
A synchronous call keeps control until it returns, whereas an asynchronous API can start host work and return before that work completes.
An async function still runs synchronously until its first suspension point; its promise represents the eventual completion.
Synchronous vs asynchronous functions
Synchronous calls complete on the current stack; asynchronous APIs arrange for later completion of operations such as I/O and network requests.
Synchronous functions
Synchronous functions execute in a sequential order, one after the other. Each operation must wait for the previous one to complete before moving on to the next.
- Synchronous code occupies the current JavaScript thread until the current operation finishes.
- It follows a strict sequence, executing instructions line by line.
- Synchronous functions are easier to understand and debug since the flow is predictable.
Synchronous function examples
-
Reading files synchronously: When reading a file from the file system using the synchronous
readFileSyncmethod from thefsmodule in Node.js, the program execution is blocked until the entire file is read. This can cause performance issues, especially for large files or when reading multiple files sequentially. -
Looping over large datasets: Iterating over a large array or dataset synchronously can freeze the user interface or browser tab until the operation completes, leading to an unresponsive application.
Asynchronous functions and APIs
Asynchronous APIs let the host wait for operations such as network or file I/O without keeping the JavaScript stack occupied. Their JavaScript callbacks still run on an event-loop thread and can block it if they do expensive work.
- It enables concurrency while waiting for host operations, often improving responsiveness.
- Declaring a function
asyncdoes not move its body to another thread. It executes immediately until anawaitactually suspends it. - Asynchronous functions are commonly used for tasks like network requests, file I/O, and timers.
Asynchronous function examples
-
Network requests: Making network requests, such as fetching data from an API or sending data to a server, is typically done asynchronously. This allows the application to remain responsive while waiting for the response, preventing the user interface from freezing.
-
User input and events: Input handlers run when the browser dispatches events such as clicks, key presses, or mouse movements. Keep handlers brief so later input and rendering can proceed.
-
Timers and animations: Timers (
setTimeout(),setInterval()) and animations (e.g.,requestAnimationFrame()) are asynchronous operations that allow the application to schedule tasks or update animations without blocking the main thread.
By using asynchronous functions and operations, JavaScript can handle time-consuming tasks without freezing the user interface or blocking the main thread.
Note that async functions do not run on a different thread. They still run on the main thread. However, it is possible to achieve parallelism in JavaScript by using Web workers.
Achieving parallelism in JavaScript via web workers
Web workers allow you to spawn separate background threads that can perform CPU-intensive tasks in parallel with the main thread. These worker threads can communicate with the main thread via message passing, but they do not have direct access to the DOM or other browser APIs.
In this example, the main thread creates a new web worker and sends it a message to start a computation. The worker performs the heavy computation in parallel with the main thread and sends the result back via postMessage().
Event loop
Within one JavaScript agent, the event loop coordinates asynchronous callbacks without running two jobs at the same time. Workers are separate agents and can execute JavaScript concurrently on other threads. Understanding that boundary explains both why a long callback blocks its own event loop and how workers provide parallelism.
Further reading
- Asynchronous JavaScript
- The Basics: synchronous and asynchronous JavaScript
- Synchronous and asynchronous programming in JavaScript
- Web Workers API - MDN
- An overview of web workers - Web.dev