---
title: "JavaScript’s Event Loop and Asynchronous Execution"
description: "Learn how JavaScript runtime environments like browsers, Node.js, Bun and Deno handle concurrency and enable asynchronous execution."
slug: "javascripts-event-loop-and-asynchronous-execution"
url: "https://furkanbaytekin.dev/blogs/javascripts-event-loop-and-asynchronous-execution"
author: "Furkan Baytekin"
category: "Software"
tags: []
published: 2025-02-04T09:58:44Z
updated: 2025-02-04T09:58:44Z
readTime: 5
cover: "https://myblogcms.furkanbaytekin.dev/uploads/cover/javascripts-event-loop-and-asynchronous-execution+1738491997151"
---

JavaScript is often described as "single-threaded" and "asynchronous," but what does that actually mean? How does JavaScript handle multiple tasks without blocking execution? In this post, we'll break down how JavaScript executes code, what the **event loop** does, and how different environments like browsers, Node.js, Bun, and Deno manage async tasks.

## Is JavaScript Single or Multi-Threaded?

JavaScript itself is **single-threaded**, meaning it can execute only one piece of code at a time in a single call stack. However, JavaScript can **handle concurrency** through **asynchronous programming**. This is possible because JavaScript doesn’t execute all tasks by itself—it **delegates** certain operations to the underlying runtime environment.

### JavaScript’s Execution Model

JavaScript follows a **run-to-completion** model, meaning that once a function starts executing, nothing else can interrupt it until it finishes.

- **Synchronous code** runs sequentially, blocking further execution until it’s done.
- **Asynchronous code** (e.g., `setTimeout`, Promises, async/await) is delegated to the environment, which schedules it to run later.

### The Role of the JavaScript Engine

A **JavaScript engine** (e.g., V8 for Chrome and Node.js, SpiderMonkey for Firefox) executes JavaScript code. The engine itself:
1. **Parses and compiles JavaScript** into machine code.
2. **Executes synchronous JavaScript** line by line.
3. **Interacts with the event loop** to handle async tasks.

However, the engine **does not handle async tasks like timers, HTTP requests, or file system operations**—that’s where the **runtime environment** comes in.

## What is a JavaScript Runtime Environment?

A **JavaScript runtime environment** provides additional APIs that JavaScript itself does not have. These environments include:

- **Browser Environment (Chrome, Firefox, Safari, Edge)**
- **Server-Side Runtimes (Node.js, Bun, Deno)**

Each environment includes:

- A **JavaScript Engine** (e.g., V8 for Chrome and Node.js, JavaScriptCore for Safari, SpiderMonkey for Firefox).
- **Additional APIs** (e.g., `fetch()`, `setTimeout()`, `fs.readFile()`).
- **An Event Loop** to manage async tasks.

## How the JavaScript Event Loop Works

The **event loop** is what enables JavaScript to handle asynchronous tasks while maintaining its single-threaded nature.

### Event Loop Components

1. **Call Stack**
  - Stores the execution context of running functions.
  - Functions run in a LIFO (Last In, First Out) manner.
  - If a function is blocking, it prevents anything else from executing.
2. **Web APIs (in Browsers) / System APIs (in Node.js, Bun, Deno)**
  - Handle tasks like timers, HTTP requests, and file I/O.
  - These tasks run in a separate thread managed by the runtime.
3. **Callback Queue**
  - Stores callbacks from async tasks that are ready to execute.
  - Callbacks wait here until the call stack is empty.

4. **Microtask Queue**
  - Higher priority than the callback queue.
  - Stores **Promises and MutationObserver callbacks**.
  - Runs immediately after the current function finishes execution.

### Step-by-Step Execution

1. **JavaScript starts executing synchronous code** in the call stack.
2. **If an asynchronous task (like `setTimeout`) is encountered**, it’s delegated to the runtime (browser or Node.js).
3. **Once the async task is complete**, its callback is added to the appropriate queue:
  - *Microtasks (Promises, async/await) run first.*
  - *Callbacks from timers or I/O operations are added to the event queue.*
4. **The event loop checks the call stack:**
  - If it's empty, it moves the next task from the queue to the call stack.
5. **The process repeats indefinitely.**

---

## Asynchronous Examples

### Example 1: setTimeout

```js
console.log("Start");

setTimeout(() => {
  console.log("Timeout callback");
}, 1000);

console.log("End");
```

#### Execution Flow:

1. `"Start"` is logged.
2. `setTimeout()` is sent to the Web API (browser) or Timer API (Node.js).
3. `"End"` is logged.
4. After 1000ms, the callback `"Timeout callback"` is added to the event queue.
5. Once the call stack is empty, the event loop moves the callback to the call stack.

**Output:**

```
Start
End
Timeout callback
```

---

### Example 2: Promises (Microtasks)

```js
console.log("Start");

setTimeout(() => console.log("Timeout callback"), 0);

Promise.resolve().then(() => console.log("Promise resolved"));

console.log("End");
```

#### Execution Flow:
1. `"Start"` is logged.
2. `setTimeout()` is sent to the Web API.
3. `Promise.resolve().then(...)` is added to the microtask queue.
4. `"End"` is logged.
5. Microtasks run before the event queue, so `"Promise resolved"` is logged.
6. The event loop picks up `"Timeout callback"` from the event queue.

**Output:**
```
Start
End
Promise resolved
Timeout callback
```

---

## JavaScript Runtime Environments

### 1. **Browser Environment**

- Provides **DOM APIs, `fetch()`, WebSockets, IndexedDB, etc.**
- Uses a **multi-threaded architecture** (though JS itself is single-threaded).
- Web APIs (like `setTimeout`, `fetch`) run in separate threads.

### 2. **Node.js**

- Uses the **V8 engine** but has its own runtime.
- Provides **server-side APIs** (`fs`, `http`, `process`).
- Uses **libuv** for async operations.
- Heavily uses **event-driven programming**.

### 3. **Bun**

- A faster alternative to Node.js.
- Built with **JavaScriptCore (not V8)**.
- Supports **native fetch, Web APIs, and built-in bundling**.
- Optimized for speed, reducing overhead.

### 4. **Deno**

- Also uses **V8** but provides a more secure runtime.
- Supports **TypeScript out of the box**.
- Uses **Web APIs like fetch** instead of Node.js-specific APIs.
- Runs with **sandboxed security** (e.g., file access must be explicitly granted).

---

## Conclusion

- JavaScript **itself** is **single-threaded**, but its runtime enables asynchronous execution.
- The **event loop** ensures non-blocking execution by delegating tasks to the environment.
- The **call stack, microtask queue, and event queue** work together to manage async tasks.
- Different **JavaScript runtimes** (browser, Node.js, Bun, Deno) provide unique APIs and optimizations.

Understanding how JavaScript handles concurrency is crucial for writing efficient, non-blocking applications. Next time you're working with async code, remember: **JavaScript doesn't do everything itself—it relies on the runtime environment!**

## The Most Helpful Video Contents

<iframe width="100%" src="https://www.youtube-nocookie.com/embed/8aGhZQkoFbQ?si=74KNucU37eXX5L1C" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<br/>
<iframe width="100%" src="https://www.youtube-nocookie.com/embed/eiC58R16hb8?si=KeygBseI-pU_jpFQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> 

---

Album of the day:

<iframe style="border-radius:12px" src="https://open.spotify.com/embed/album/3xIpxM4JMvq0vHjtQhgncJ?utm_source=generator" width="100%" height="152" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>
