quiz: review event loop questions
This commit is contained in:
parent
8417e54e1f
commit
4cb2ceb156
Binary file not shown.
|
Before Width: | Height: | Size: 888 KiB |
|
|
@ -1,131 +0,0 @@
|
|||
---
|
||||
title: What are microtasks and macrotasks?
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
In JavaScript, tasks are managed by the event loop and are categorized into:
|
||||
|
||||
1. **Macrotasks**:
|
||||
|
||||
- Higher-level events like script execution, user interactions, timers, etc.
|
||||
- Added to the macrotask queue and executed one by one.
|
||||
|
||||
2. **Microtasks**:
|
||||
|
||||
- Lower-level tasks like promise handlers, mutation observers, etc.
|
||||
- These have higher priority and are executed immediately after the current script but before any macrotask.
|
||||
|
||||
### Event Loop Steps:
|
||||
|
||||
- Execute the current macrotask.
|
||||
- Process all microtasks.
|
||||
- Render updates (in a browser).
|
||||
- Pick the next macrotask and repeat.
|
||||
|
||||
```js
|
||||
console.log('start'); // script
|
||||
setTimeout(() => console.log('setTimeout'), 0); // macrotask
|
||||
Promise.resolve().then(() => console.log('Promise')); // microtask
|
||||
console.log('end'); // script
|
||||
|
||||
// output
|
||||
// start
|
||||
// end
|
||||
// Promise
|
||||
// setTimeout
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Macrotask and microtask in JavaScript
|
||||
|
||||
In JavaScript, microtasks and macrotasks are two types of tasks that are managed by the event loop, which is responsible for executing code, handling events, and managing asynchronous operations
|
||||
|
||||
### Macrotasks
|
||||
|
||||
Macrotasks, also known as tasks, include operations that are queued in the event loop's `task queue`. These tasks are generally more substantial and can include:
|
||||
|
||||
- setTimeout(callback, delay)
|
||||
- setInterval(callback, delay)
|
||||
- setImmediate(callback) (Node.js only)
|
||||
- I/O operations (e.g., file system operations in Node.js, network requests)
|
||||
- UI rendering and other browser-specific tasks
|
||||
|
||||
When the event loop processes the macrotask queue, it picks and executes the tasks one by one. After a macrotask completes, the event loop checks the microtask queue first and processes any microtasks before moving on to the next macrotask. So macrotasks have lower priority than microtasks.
|
||||
|
||||
```js
|
||||
console.log('Script start');
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('setTimeout callback');
|
||||
}, 0);
|
||||
|
||||
console.log('Script end');
|
||||
|
||||
// Output:
|
||||
// Script start
|
||||
// Script end
|
||||
// setTimeout callback
|
||||
```
|
||||
|
||||
### Microtasks
|
||||
|
||||
Microtasks are smaller tasks that are usually executed immediately after the currently executing script and before any macrotask is executed. Microtasks typically include:
|
||||
|
||||
- Promises (e.g., Promise.then and Promise.catch callbacks)
|
||||
- `MutationObserver` callbacks
|
||||
- `queueMicrotask` (callback)
|
||||
|
||||
Microtasks are generally intended for short, quick tasks that need to be executed soon after the current operation completes. They have a higher priority than macrotasks, meaning they are executed before the event loop moves on to the next macrotask.
|
||||
|
||||
```js
|
||||
console.log('Script start');
|
||||
|
||||
Promise.resolve()
|
||||
.then(() => console.log('Promise microtask'))
|
||||
.catch((err) => console.error(err));
|
||||
|
||||
console.log('Script end');
|
||||
|
||||
// Output:
|
||||
// Script start
|
||||
// Script end
|
||||
// Promise microtask
|
||||
```
|
||||
|
||||
### Event Loop Process
|
||||
|
||||
The event loop follows this order of execution:
|
||||
|
||||
- Execute the current macrotask.
|
||||
- Execute all microtasks in the microtask queue.
|
||||
- Render any updates (if applicable).
|
||||
- Move on to the next macrotask.
|
||||
|
||||
This process repeats until there are no more tasks to execute
|
||||
|
||||
```js
|
||||
console.log('Script start');
|
||||
|
||||
setTimeout(() => console.log('setTimeout'), 0);
|
||||
|
||||
queueMicrotask(() => console.log('queueMicrotask microtask'));
|
||||
|
||||
console.log('Script end');
|
||||
|
||||
// Output:
|
||||
// Script start
|
||||
// Script end
|
||||
// queueMicrotask microtask
|
||||
// setTimeout
|
||||
```
|
||||
|
||||
The key difference is that microtasks have a higher priority than macrotasks and are executed immediately after the current macrotask, before rendering or moving on to the next macrotask. This allows for more precise and responsive handling of asynchronous operations, such as promises
|
||||
|
||||
## Further reading
|
||||
|
||||
- [The event loop - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop)
|
||||
- [Tasks, microtasks, queues and schedules](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/)
|
||||
- [Unraveling Macrotasks and Microtasks in JavaScript: What Every Web Developer Should Know](https://dev.to/bymarsel/unraveling-macrotasks-and-microtasks-in-javascript-what-every-developer-should-know-53mc)
|
||||
- [https://javascript.info/event-loop](https://javascript.info/event-loop)
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"slug": "what-are-microtasks-and-macrotasks",
|
||||
"languages": [],
|
||||
"companies": [],
|
||||
"premium": false,
|
||||
"duration": 5,
|
||||
"published": true,
|
||||
"topics": ["javascript"],
|
||||
"importance": "high",
|
||||
"difficulty": "medium"
|
||||
}
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
---
|
||||
title: What are server sent events ?
|
||||
title: What are server-sent events?
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
[`Server-sent events (SSE)`](https://html.spec.whatwg.org/multipage/comms.html#the-eventsource-interface) is a standard that allows a web page to receive automatic updates from a server via an HTTP connection. `Server-sent events` describe a built-in-class `EventSource` that keeps connection with the server and allows client to receive events from server. Connection created by `Server-sent events` are persistent similar to the `WebSocket`, however there are several differences:
|
||||
[Server-sent events (SSE)](https://html.spec.whatwg.org/multipage/comms.html#the-eventsource-interface) is a standard that allows a web page to receive automatic updates from a server via an HTTP connection. Server-sent events are used with `EventSource` instances that opens a connection with a server and allows client to receive events from the server. Connections created by server-sent events are persistent (similar to the `WebSocket`s), however there are a few differences:
|
||||
|
||||
| WebSocket | EventSource |
|
||||
| --- | --- |
|
||||
| Bi-directional: both client and server can exchange messages | One-directional: only server sends data |
|
||||
| Binary and text data | Only text |
|
||||
| WebSocket protocol | Regular HTTP |
|
||||
| Property | `WebSocket` | `EventSource` |
|
||||
| --- | --- | --- |
|
||||
| Direction | Bi-directional – both client and server can exchange messages | Unidirectional – only server sends data |
|
||||
| Data type | Binary and text data | Only text |
|
||||
| Protocol | WebSocket protocol (`ws://`) | Regular HTTP (`http://`) |
|
||||
|
||||
**Creating an EventSource**
|
||||
**Creating an event source**
|
||||
|
||||
```js
|
||||
const eventSource = new EventSource('/sse-stream');
|
||||
|
|
@ -26,7 +26,7 @@ eventSource.addEventListener('open', () => {
|
|||
console.log('Connection opened');
|
||||
});
|
||||
|
||||
// Fired when a message is received from the server
|
||||
// Fired when a message is received from the server.
|
||||
eventSource.addEventListener('message', (event) => {
|
||||
console.log('Received message:', event.data);
|
||||
});
|
||||
|
|
@ -44,12 +44,12 @@ const express = require('express');
|
|||
const app = express();
|
||||
|
||||
app.get('/sse-stream', (req, res) => {
|
||||
// `Content-Type` need to be set to `text/event-stream`
|
||||
// `Content-Type` need to be set to `text/event-stream`.
|
||||
res.setHeader('Content-Type', 'text/event-stream');
|
||||
res.setHeader('Cache-Control', 'no-cache');
|
||||
res.setHeader('Connection', 'keep-alive');
|
||||
|
||||
// Each message should be prefixed with data
|
||||
// Each message should be prefixed with data.
|
||||
const sendEvent = (data) => res.write(`data: ${data}\n\n`);
|
||||
|
||||
sendEvent('Hello from server');
|
||||
|
|
@ -69,59 +69,63 @@ In this example, the server sends a "Hello from server" message initially, and t
|
|||
|
||||
---
|
||||
|
||||
## `Server-sent events`
|
||||
## Server-sent events (SSE)
|
||||
|
||||
Server-Sent Events (SSE) is a standard that allows a server to push updates to a web client over a single, long-lived HTTP connection. It enables real-time updates without the client having to constantly poll the server for new data.
|
||||
Server-sent events (SSE) is a standard that allows a server to push updates to a web client over a single, long-lived HTTP connection. It enables real-time updates without the client having to constantly poll the server for new data.
|
||||
|
||||
### How `SSE` works:
|
||||
## How SSE works
|
||||
|
||||
1. The client creates a new `EventSource` object, passing the URL of the `server-side` script that will generate the event stream:
|
||||
|
||||
```js
|
||||
const eventSource = new EventSource('/event-stream');
|
||||
```
|
||||
```js
|
||||
const eventSource = new EventSource('/event-stream');
|
||||
```
|
||||
|
||||
2. The server-side script sets the appropriate headers to indicate that it will be sending an event stream (`Content-Type: text/event-stream`), and then starts sending events to the client.
|
||||
3. Each event sent by the server follows a specific format, with fields like `event`, `data`, and `id`. For example:
|
||||
|
||||
```js
|
||||
event: message
|
||||
data: Hello, world!
|
||||
```js
|
||||
event: message
|
||||
data: Hello, world!
|
||||
|
||||
event: update
|
||||
id: 123
|
||||
data: {"temperature": 25, "humidity": 60}
|
||||
```
|
||||
event: update
|
||||
id: 123
|
||||
data: {"temperature": 25, "humidity": 60}
|
||||
```
|
||||
|
||||
4. On the client-side, the `EventSource` object receives these events and dispatches them as `DOM` events, which can be handled using event listeners or the `onmessage` event handler:
|
||||
4. On the client-side, the `EventSource` object receives these events and dispatches them as browser events, which can be handled using event listeners or the `onmessage` event handler:
|
||||
|
||||
```js
|
||||
eventSource.onmessage = function (event) {
|
||||
console.log('Received message:', event.data);
|
||||
};
|
||||
```js
|
||||
eventSource.onmessage = function (event) {
|
||||
console.log('Received message:', event.data);
|
||||
};
|
||||
|
||||
eventSource.addEventListener('update', function (event) {
|
||||
console.log('Received update:', JSON.parse(event.data));
|
||||
});
|
||||
```
|
||||
eventSource.addEventListener('update', function (event) {
|
||||
console.log('Received update:', JSON.parse(event.data));
|
||||
});
|
||||
```
|
||||
|
||||
5. The `EventSource` object automatically handles reconnection if the connection is lost, and it can resume the event stream from the last received event ID using the `Last-Event-ID HTTP header`.
|
||||
|
||||
### Advantages of `SSE`
|
||||
## SSE features
|
||||
|
||||
- **Simpler than WebSockets**: Easier to implement and understand than `WebSockets` for many use cases.
|
||||
- **Automatic Reconnection**: The client handles reconnections automatically.
|
||||
- **Lightweight**: Uses simple text-based messages and a single `HTTP` connection.
|
||||
- **Built-in Browser Support**: Supported by most modern browsers without additional libraries.
|
||||
- **Unidirectional**: Only the server can send data to the client. For bidirectional communication, web sockets would be more appropriate.
|
||||
- **Retry mechanism**: The client will retry the connection if it fails, with the retry interval specified by the `retry:` field from the server.
|
||||
- **Text-only data**: SSE can only transmit text data, which means binary data needs to be encoded (e.g., Base64) before transmission. This can lead to increased overhead and inefficiency for applications that need to transmit large binary payloads.
|
||||
- **Built-in browser support**: Supported by most modern browsers without additional libraries.
|
||||
- **Event types**: SSE supports custom event types using the `event:` field, allowing categorization of messages.
|
||||
- **`Last-Event-Id`**: The client sends the `Last-Event-Id` header when reconnecting, allowing the server to resume the stream from the last received event. However, there is no built-in mechanism to replay missed events during the disconnection period. You may need to implement a mechanism to handle missed events, such as using the `Last-Event-Id` header.
|
||||
- **Connection limitations**: Browsers have a limit on the maximum number of concurrent SSE connections, typically around 6 per domain. This can be a bottleneck if you need to establish multiple SSE connections from the same client. Using HTTP/2 will mitigate this issue.
|
||||
|
||||
### Disadvantages of SSE
|
||||
## Implementing SSE in JavaScript
|
||||
|
||||
- **Unidirectional**: Only the server can send data to the client. For bidirectional communication, WebSockets would be more appropriate.
|
||||
- **Connection Limitations**: Limited to the maximum number of open `HTTP` connections per browser, which can be an issue with many clients.
|
||||
The following code demonstrates a minimal implementation of SSE on the client and the server:
|
||||
|
||||
### Implementing `SSE` in JavaScript
|
||||
- The server sets the appropriate headers to establish an SSE connection.
|
||||
- Messages are sent to the client every 5 seconds.
|
||||
- The server cleans up the interval and ends the response when the client disconnects.
|
||||
|
||||
#### Client-Side Code:
|
||||
On the client:
|
||||
|
||||
```js
|
||||
// Create a new EventSource object
|
||||
|
|
@ -143,7 +147,7 @@ eventSource.onopen = function () {
|
|||
};
|
||||
```
|
||||
|
||||
#### Server-Side Code:
|
||||
On the server:
|
||||
|
||||
```js
|
||||
const http = require('http');
|
||||
|
|
@ -160,7 +164,7 @@ http
|
|||
|
||||
// Function to send a message
|
||||
const sendMessage = (message) => {
|
||||
res.write(`data: ${message}\n\n`);
|
||||
res.write(`data: ${message}\n\n`); // Messages are delimited with double line breaks.
|
||||
};
|
||||
|
||||
// Send a message every 5 seconds
|
||||
|
|
@ -183,26 +187,13 @@ http
|
|||
});
|
||||
```
|
||||
|
||||
In this example:
|
||||
|
||||
- The server sets the appropriate headers to establish an `SSE` connection.
|
||||
- Messages are sent to the client every 5 seconds.
|
||||
- The server cleans up the interval and ends the response when the client disconnects.
|
||||
|
||||
### Considerations
|
||||
|
||||
- **Event Types**: `SSE` supports custom event types using the `event:` field, allowing you to categorize messages.
|
||||
- **Retry Mechanism**: The client will retry the connection if it fails, with the retry interval specified by the `retry:` field from the server.
|
||||
- **Last-Event-ID**: The client sends the `Last-Event-ID` header when reconnecting, allowing the server to resume the stream from the last received event.
|
||||
- **CORS and Authentication**: Ensure `CORS` headers are correctly configured for cross-origin requests, and consider secure methods for authentication and authorization.
|
||||
|
||||
## Summary
|
||||
|
||||
`Server-sent Events` provide an efficient and straightforward way to push updates from a server to a client in real-time. They are particularly well-suited for applications that require continuous data streams but do not need full `bidirectional` communication. With built-in support in modern browsers, `SSE` is a reliable choice for many real-time web applications.
|
||||
Server-sent events provide an efficient and straightforward way to push updates from a server to a client in real-time. They are particularly well-suited for applications that require continuous data streams but do not need full bidirectional communication. With built-in support in modern browsers, SSE is a reliable choice for many real-time web applications.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Using server-sent events - MDN](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)
|
||||
- [Server sent events - javascript.info](https://javascript.info/server-sent-events)
|
||||
- [Server-Sent Events: A webSockets alternative ready for another look](https://ably.com/topic/server-sent-events)
|
||||
- [Server-sent Events - javascript.info](https://javascript.info/server-sent-events)
|
||||
- [Server-sent Events: A WebSockets alternative ready for another look](https://ably.com/topic/server-sent-events)
|
||||
- [What are SSE (Server-Sent Events) and how do they work?](https://bunny.net/academy/http/what-is-sse-server-sent-events-and-how-do-they-work/)
|
||||
|
|
|
|||
|
|
@ -1,31 +1,30 @@
|
|||
---
|
||||
title: What are workers in javascript used for?
|
||||
title: What are workers in JavaScript used for?
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
Workers in JavaScript are background threads that allow you to run scripts in parallel with the main execution thread, without blocking or interfering with the user interface.
|
||||
Workers in JavaScript are background threads that allow you to run scripts in parallel with the main execution thread, without blocking or interfering with the user interface. Their key features include:
|
||||
|
||||
### Key features
|
||||
- **Parallel processing**: Workers run in a separate thread from the main thread, allowing your web page to remain responsive to user interactions while the worker performs its tasks. It's useful for moving CPU-intensive work off the main thread and be free from JavaScript's single-threaded nature.
|
||||
- **Communication**: Uses `postMessage()` and `onmessage`/ `'message'` event for messaging.
|
||||
- **Access to web APIs**: Workers have access to various Web APIs, including `fetch()`, IndexedDB, and Web Storage, allowing them to perform tasks like data fetching and persisting data independently.
|
||||
- **No DOM access**: Workers cannot directly manipulate the DOM, thus cannot interact with the UI, ensuring they don't accidentally interfere with the main thread's operation.
|
||||
|
||||
- **Parallel Processing** : Workers run in a separate thread from the main thread, allowing your web page to remain responsive to user interactions while the worker performs its tasks.
|
||||
- **Communication** : Uses `postMessage` and `onmessage` for messaging.
|
||||
- **Access to Web APIs**: Workers have access to various Web APIs, including `Fetch` API, `IndexedDB`, and `LocalStorage`, allowing them to perform tasks like data fetching and persisting data independently.
|
||||
- **No DOM Access** : Workers cannot directly manipulate the `DOM` or interact with the `UI`, ensuring they don't accidentally interfere with the main thread's operation.
|
||||
There are three main types of workers in JavaScript:
|
||||
|
||||
There are two main types of workers:
|
||||
|
||||
### Web workers
|
||||
|
||||
- Run scripts in background threads, separate from the main `UI` thread.
|
||||
- Useful for CPU-intensive tasks like data processing, calculations, etc.
|
||||
- Cannot directly access or manipulate the `DOM`.
|
||||
|
||||
### Service Workers:
|
||||
|
||||
- Act as network proxies, handling requests between the app and network.
|
||||
- Enable offline functionality, caching, and push notifications.
|
||||
- Run independently of the web app, even when it's closed.
|
||||
- **Web workers / Dedicated workers**
|
||||
- Run scripts in background threads, separate from the main UI thread.
|
||||
- Useful for CPU-intensive tasks like data processing, calculations, etc.
|
||||
- Cannot directly access or manipulate the DOM.
|
||||
- **Service workers**
|
||||
- Act as network proxies, handling requests between the app and network.
|
||||
- Enable offline functionality, caching, and push notifications.
|
||||
- Runs independently of the web page, even when it's closed.
|
||||
- **Shared workers**
|
||||
- Can be shared by multiple scripts running in different windows or frames, as long as they're in the same domain.
|
||||
- Scripts communicate with the shared worker by sending and receiving messages.
|
||||
- Useful for coordinating tasks across different parts of a web page.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -33,27 +32,25 @@ There are two main types of workers:
|
|||
|
||||
Workers in JavaScript are a way to run scripts in background threads, separate from the main execution thread of a web page. This allows for long-running or computationally intensive tasks to be offloaded from the main thread, preventing the user interface from becoming unresponsive or janky.
|
||||
|
||||
## Types of Workers
|
||||
## Web workers / Dedicated workers
|
||||
|
||||
#### Web Workers
|
||||
- Run scripts in background threads separate from the main UI thread.
|
||||
- Designed for CPU-intensive tasks like data processing, mathematical computations, etc. Generally the non-async work.
|
||||
- Cannot directly access the DOM or other main thread resources for security.
|
||||
- Communicates with main thread via asynchronous message passing – `postMessage()` and `onmessage`/ `'message'`.
|
||||
- Terminated when main script is unloaded or explicitly terminated.
|
||||
|
||||
- Run scripts in background threads separate from the main `UI` thread
|
||||
- Designed for CPU-intensive tasks like data processing, computations, etc.
|
||||
- Cannot directly access the `DOM` or other main thread resources for security
|
||||
- Communicate with main thread via asynchronous message passing
|
||||
- Terminated when main script is unloaded or explicitly terminated
|
||||
|
||||
#### Use cases for web worker :
|
||||
Web workers can be used for:
|
||||
|
||||
- Image/video processing
|
||||
- Data compression
|
||||
- Complex math
|
||||
|
||||
#### Creating a Web Worker
|
||||
### Creating a web worker
|
||||
|
||||
To create a web worker, you need a separate JavaScript file that contains the code for the worker. Here's an example:
|
||||
|
||||
**main.js (main script)**
|
||||
**`main.js` (main script)**
|
||||
|
||||
```js
|
||||
// Check if the browser supports workers
|
||||
|
|
@ -76,7 +73,7 @@ if (window.Worker) {
|
|||
}
|
||||
```
|
||||
|
||||
**worker.js (worker script)**
|
||||
**`worker.js` (worker script)**
|
||||
|
||||
```js
|
||||
// Listen for messages from the main script
|
||||
|
|
@ -93,30 +90,30 @@ onmessage = function (event) {
|
|||
|
||||
In this example:
|
||||
|
||||
- `main.js` creates a worker using the `Worker` constructor and specifies `worker.j`s as the script to run in the worker thread.
|
||||
- It posts a message to the worker using `postMessage`.
|
||||
- `main.js` creates a worker using the `Worker` constructor and specifies `worker.js` as the script to run in the worker thread.
|
||||
- It posts a message to the worker using `postMessage()`.
|
||||
- The worker script (`worker.js`) listens for messages from the main script using `onmessage`.
|
||||
- After processing the message, the worker posts a message back to the main script using `postMessage`.
|
||||
- After processing the message, the worker posts a message back to the main script using `postMessage()`.
|
||||
- The main script listens for messages from the worker using `onmessage` on the `Worker` instance.
|
||||
|
||||
### Service Workers
|
||||
## Service workers
|
||||
|
||||
- Act as a network proxy between web app, browser, and network
|
||||
- Can intercept and handle network requests, cache resources
|
||||
- Enable offline functionality and push notifications
|
||||
- Have a lifecycle managed by the browser (install, activate, update)
|
||||
- Limited access to `DOM` and main thread resources for security
|
||||
- Act as a network proxy between web app, browser, and network.
|
||||
- Can intercept and handle network requests, cache resources.
|
||||
- Enable offline functionality and push notifications.
|
||||
- Have a lifecycle managed by the browser (install, activate, update).
|
||||
- No access to DOM and main thread resources for security.
|
||||
|
||||
#### Use cases for service workers:
|
||||
Service workers can be used for:
|
||||
|
||||
- Caching
|
||||
- Offline support
|
||||
- Request handling
|
||||
- Background sync
|
||||
|
||||
#### Creating a service Worker
|
||||
#### Creating a service worker
|
||||
|
||||
**In main.js (main script)**
|
||||
**`main.js` (main script)**
|
||||
|
||||
```js
|
||||
if ('serviceWorker' in navigator) {
|
||||
|
|
@ -131,7 +128,7 @@ if ('serviceWorker' in navigator) {
|
|||
}
|
||||
```
|
||||
|
||||
**service-worker.js (service worker script)**
|
||||
**`service-worker.js` (service worker script)**
|
||||
|
||||
```js
|
||||
self.addEventListener('fetch', function (event) {
|
||||
|
|
@ -151,51 +148,38 @@ self.addEventListener('fetch', function (event) {
|
|||
|
||||
In this example:
|
||||
|
||||
- The main script registers a `Service worker` at `/service-worker.js`.
|
||||
- The Service worker listens for the `fetch` event, which is fired whenever the browser makes a network request.
|
||||
- The Service worker first checks if the requested resource is cached using `caches.match(event.request)`.
|
||||
- The main script registers a service worker at `/service-worker.js`.
|
||||
- The service worker listens for the `fetch()` event, which is fired whenever the browser makes a network request.
|
||||
- The service worker first checks if the requested resource is cached using `caches.match(event.request)`.
|
||||
- If it is, it returns the cached response. Otherwise, it fetches the resource from the network using `fetch(event.request)`.
|
||||
|
||||
### Shared Workers
|
||||
## Shared workers
|
||||
|
||||
- Can be accessed from multiple scripts in different windows/tabs/iframes
|
||||
- Allow data sharing between browser contexts via a messaging interface
|
||||
- Similar to dedicated Web Workers but with a broader scope
|
||||
- Can be accessed from multiple scripts in different windows/tabs/iframes.
|
||||
- Allow data sharing between browser contexts via a messaging interface.
|
||||
- Similar to dedicated web workers but with a broader scope.
|
||||
|
||||
### Use cases for service workers:
|
||||
### Use cases for shared workers:
|
||||
|
||||
- State sharing across multiple windows
|
||||
- State sharing across multiple windows.
|
||||
|
||||
### Worklets
|
||||
### Bonus: Worklets
|
||||
|
||||
- Lightweight workers for specific use cases like painting, audio processing
|
||||
- Run in separate threads from main UI thread
|
||||
- Provide a way to extend browser rendering engines with custom logic
|
||||
- **Types**: Animation Worklets, Audio Worklets, Layout Worklets, Paint Worklets
|
||||
The `Worklet` interface is a lightweight version of Web Workers and gives developers access to low-level parts of the rendering pipeline. With Worklets, you can run JavaScript and WebAssembly code to do graphics rendering or audio processing where high performance is required.
|
||||
|
||||
### Use cases for worklet:
|
||||
You are not expected to know about worklets, so it won't be covered in great detail. Read more about [worklets on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Worklet).
|
||||
|
||||
- Custom CSS animations
|
||||
- Audio effects
|
||||
- Layout calculations
|
||||
## Considerations and limitations
|
||||
|
||||
The key differences lie in their intended purposes, scope, and access to browser APIs. Web Workers are for CPU-intensive tasks, Service Workers handle network requests and offline, Shared Workers enable cross-window communication, and Worklets extend browser rendering capabilities.
|
||||
|
||||
## Considerations and Limitations
|
||||
|
||||
- **Same-Origin Policy**: Workers must comply with the same-origin policy, meaning the script that creates the worker and the worker script itself must be from the same origin.
|
||||
- **No DOM Access**: Workers do not have direct access to the DOM. They can communicate with the main thread through messages.
|
||||
- **Same-Origin policy**: Workers must comply with the same-origin policy, meaning the script that creates the worker and the worker script itself must be from the same origin.
|
||||
- **No DOM access**: Workers do not have direct access to the DOM. They can communicate with the main thread through messages.
|
||||
- **Performance**: Creating and managing workers incurs overhead. They should be used judiciously for tasks that truly benefit from parallel execution.
|
||||
- **Error Handling**: Proper error handling mechanisms should be in place to handle any issues within the worker scripts.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Workers in JavaScript are a powerful tool for improving the performance and responsiveness of web applications by offloading time-consuming tasks to background threads. They enable more efficient and smoother user experiences, especially for applications requiring heavy computations or asynchronous processing. By understanding and utilizing workers effectively, developers can create more robust and high-performing web applications.
|
||||
- **Error handling**: Proper error handling mechanisms should be in place to handle any issues within the worker scripts.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Worker - MDN](https://developer.mozilla.org/en-US/docs/Web/API/Worker)
|
||||
- [Using Web Workers - MDN](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers)
|
||||
- [Service Worker API - MDN](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
|
||||
- [Understanding web workers in JavaScript with real world example ](https://javascript.plainenglish.io/understanding-web-workers-in-js-with-real-world-example-1629a283aeec?gi=89cf0dd4c3f4)
|
||||
- [Understanding web workers in JavaScript with real world example](https://javascript.plainenglish.io/understanding-web-workers-in-js-with-real-world-example-1629a283aeec?gi=89cf0dd4c3f4)
|
||||
- [Web worker, Service worker, and Worklets: A comprehensive guide](https://dev.to/bharat5604/web-worker-service-worker-and-worklets-a-comprehensive-guide-1f64)
|
||||
|
|
|
|||
|
|
@ -5,61 +5,67 @@ subtitle: What is the difference between call stack and task queue?
|
|||
|
||||
## TL;DR
|
||||
|
||||
The event loop is a single-threaded loop that monitors the call stack and checks if there is any work to be done in the task queue. If the call stack is empty and there are callback functions in the task queue, a function is dequeued and pushed onto the call stack to be executed.
|
||||
The event loop is concept within the browser runtime environment regarding how asynchronous operations are executed within JavaScript engines. It works as such:
|
||||
|
||||
If you haven't already checked out Philip Robert's [talk on the Event Loop](https://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html), you should. It is one of the most viewed videos on JavaScript.
|
||||
1. The JavaScript engine starts executing scripts, placing synchronous operations on the call stack.
|
||||
2. When an asynchronous operation is encountered (e.g., `setTimeout()`, HTTP request), it is offloaded to the respective Web API or Node.js API to handle the operation in the background.
|
||||
3. Once the asynchronous operation completes, its callback function is placed in the respective queues – task queues (also known as macrotask queues / callback queues) or microtask queues. We will refer to "task queue" as "macrotask queue" from here on to better differentiate from the microtask queue.
|
||||
4. The event loop continuously monitors the call stack and executes items on the call stack. If/when the call stack is empty:
|
||||
1. Microtask queue is processed. Microtasks include promise callbacks (`then`, `catch`, `finally`), `MutationObserver` callbacks, and calls to `queueMicrotask()`. The event loop takes the first callback from the microtask queue and pushes it to the call stack for execution. This repeats until the microtask queue is empty.
|
||||
2. Macrotask queue is processed. Macrotasks include web APIs like `setTimeout()`, HTTP requests, user interface event handlers like clicks, scrolls, etc. The event loop dequeues the first callback from the macrotask queue and pushes it onto the call stack for execution. However, after a macrotask queue callback is processed, the event loop does not proceed with the next macrotask yet! The event loop first checks the microtask queue. Checking the microtask queue is necessary as microtasks have higher priority than macrotask queue callbacks. The macrotask queue callback that was just executed could have added more microtasks!
|
||||
1. If the microtask queue is non-empty, process them as per the previous step.
|
||||
2. If the microtask queue is empty, the next macrotask queue callback is processed. This repeats until the macrotask queue is empty.
|
||||
5. This process continues indefinitely, allowing the JavaScript engine to handle both synchronous and asynchronous operations efficiently without blocking the call stack.
|
||||
|
||||
For a visual demonstration of the event loop, accounting for different types of tasks, check out Jake Archibald's talk titled ["In The Loop"](https://www.youtube.com/watch?v=cCOL7MC4Pl0).
|
||||
The unfortunate truth is that it is extremely hard to explain the event loop well using only text. We recommend checking out one of the following excellent videos explaining the event loop:
|
||||
|
||||
- [JavaScript Visualized - Event Loop, Web APIs, (Micro)task Queue](https://www.youtube.com/watch?v=eiC58R16hb8) (2024): Lydia Hallie is a popular educator on JavaScript and this is the best recent videos explaining the event loop. There's also an [accompanying blog post](https://www.lydiahallie.com/blog/event-loop) for those who prefer detailed text-based explanations.
|
||||
- [In the Loop](https://www.youtube.com/watch?v=cCOL7MC4Pl0) (2018): Jake Archibald previously from the Chrome team provides a visual demonstration of the event loop during JSConf 2018, accounting for different types of tasks.
|
||||
- [What the heck is the event loop anyway?](https://www.youtube.com/watch?v=8aGhZQkoFbQ) (2014): Philip Robert's gave this epic talk at JSConf 2014 and it is one of the most viewed JavaScript videos on YouTube.
|
||||
|
||||
We recommend watching [Lydia's video](https://www.youtube.com/watch?v=eiC58R16hb8) as it is the most modern and concise explanation standing at only 13 minutes long whereas the other videos are at least 30 minutes long. Her video is sufficient for the purpose of interviews.
|
||||
|
||||
---
|
||||
|
||||
## Event loop in JavaScript
|
||||
|
||||
The event loop is the heart of JavaScript's asynchronous operation. It's is a mechanism in JavaScript that handles the execution of code, allowing for asynchronous operations and ensuring that the single-threaded nature of JavaScript does not block the execution of the program.
|
||||
The event loop is the heart of JavaScript's asynchronous operation. It is a mechanism in browsers that handles the execution of code, allowing for asynchronous operations and ensuring that the single-threaded nature of JavaScript engines does not block the execution of the program.
|
||||
|
||||
To understand it better we need to understand about all the parts of the system. Here is an excellent illustration by [Lydia Hallie](https://x.com/lydiahallie)
|
||||
### Parts of the event loop
|
||||
|
||||

|
||||
To understand it better we need to understand about all the parts of the system. These components are part of the event loop:
|
||||
|
||||
### Call stack
|
||||
#### Call stack
|
||||
|
||||
Call stack keeps track of the functions being executed in a program. When a function is called, it is added to the top of the call stack. When the function completes, it is removed from the call stack. This allows the program to keep track of where it is in the execution of a function and return to the correct location when the function completes. As the name suggests it is a `Stack` data structure which follows `last in first out`
|
||||
Call stack keeps track of the functions being executed in a program. When a function is called, it is added to the top of the call stack. When the function completes, it is removed from the call stack. This allows the program to keep track of where it is in the execution of a function and return to the correct location when the function completes. As the name suggests it is a Stack data structure which follows last-in-first-out.
|
||||
|
||||
### Task queue
|
||||
#### Web APIs/Node.js APIs
|
||||
|
||||
The task queue, also known as the macro task queue or event queue, is a queue that holds tasks that need to be executed. These tasks are typically asynchronous operations, such as setTimeout, setInterval, and event handlers.
|
||||
Asynchronous operations like `setTimeout()`, HTTP requests, file I/O, etc., are handled by Web APIs (in the browser) or C++ APIs (in Node.js). These APIs are not part of the JavaScript engine and run on separate threads, allowing them to execute concurrently without blocking the call stack.
|
||||
|
||||
### Macro tasks
|
||||
#### Task queue / Macrotask queue / Callback queue
|
||||
|
||||
Macro tasks are tasks that are added to the task queue and executed one by one in the order they were added. Point to note here is that these macro tasks will be executed after `micro tasks`
|
||||
The task queue, also known as the macrotask queue / callback queue / event queue, is a queue that holds tasks that need to be executed. These tasks are typically asynchronous operations, such as callbacks passed to web APIs (`setTimeout()`, `setInterval()`, HTTP requests, etc.), and user interface event handlers like clicks, scrolls, etc.
|
||||
|
||||
#### Macro task examples
|
||||
#### Microtasks queue
|
||||
|
||||
- setTimeout
|
||||
- setInterval
|
||||
- I/O events
|
||||
- UI rendering (in browsers)
|
||||
Microtasks are tasks that have a higher priority than macrotasks and are executed immediately after the currently executing script is completed and before the next macrotask is executed. Microtasks are usually used for more immediate, lightweight operations that should be executed as soon as possible after the current operation completes. There is a dedicated microtask queue for microtasks. Microtasks include promises callbacks (`then()`, `catch()`, and `finally()`), `await` statements, `queueMicrotask()`, and `MutationObserver` callbacks.
|
||||
|
||||
### Micro tasks
|
||||
### Event loop order
|
||||
|
||||
Micro tasks are tasks that have a higher priority than macro tasks and are executed immediately after the currently executing script is completed and before the next macro task is executed. Micro tasks are usually used for more immediate, lightweight operations that should be executed as soon as possible after the current operation completes. There is a dedicated `micro task queue` for micro tasks.
|
||||
1. The JavaScript engine starts executing scripts, placing synchronous operations on the call stack.
|
||||
2. When an asynchronous operation is encountered (e.g., `setTimeout()`, HTTP request), it is offloaded to the respective Web API or Node.js API to handle the operation in the background.
|
||||
3. Once the asynchronous operation completes, its callback function is placed in the respective queues – task queues (also known as macrotask queues / callback queues) or microtask queues. We will refer to "task queue" as "macrotask queue" from here on to better differentiate from the microtask queue.
|
||||
4. The event loop continuously monitors the call stack and executes items on the call stack. If/when the call stack is empty:
|
||||
1. Microtask queue is processed. The event loop takes the first callback from the microtask queue and pushes it to the call stack for execution. This repeats until the microtask queue is empty.
|
||||
2. Macrotask queue is processed. The event loop dequeues the first callback from the macrotask queue and pushes it onto the call stack for execution. However, after a macrotask queue callback is processed, the event loop does not proceed with the next macrotask yet! The event loop first checks the microtask queue. Checking the microtask queue is necessary as microtasks have higher priority than macrotask queue callbacks. The macrotask queue callback that was just executed could have added more microtasks!
|
||||
1. If the microtask queue is non-empty, process them as per the previous step.
|
||||
2. If the microtask queue is empty, the next macrotask queue callback is processed. This repeats until the macrotask queue is empty.
|
||||
5. This process continues indefinitely, allowing the JavaScript engine to handle both synchronous and asynchronous operations efficiently without blocking the call stack.
|
||||
|
||||
#### Micro task examples
|
||||
### Example
|
||||
|
||||
- Promises (with then, catch, and finally)
|
||||
- MutationObserver callbacks
|
||||
|
||||
## How everything work together
|
||||
|
||||
1. **Execution Starts**: JavaScript starts executing the code in the global context.
|
||||
2. **Call Stack**: As functions are called, they are pushed onto the call stack.
|
||||
3. **Async Operations**: When an asynchronous operation is encountered (e.g., setTimeout, a promise), the corresponding callback is sent to the appropriate task queue.
|
||||
4. **Event Loop**: The event loop continuously checks if the call stack is empty.
|
||||
5. **Micro Tasks First**: If the call stack is empty, the event loop first processes all the micro tasks.
|
||||
6. **Macro Tasks**: Once all micro tasks are processed, the event loop will then take the first macro task from the task queue and push it onto the call stack for execution.
|
||||
7. **Repeat**: This process repeats, ensuring a smooth and controlled execution of asynchronous operations.
|
||||
|
||||
## Example
|
||||
The following code logs some statements using a combination of normal execution, macrotasks, and microtasks.
|
||||
|
||||
```js
|
||||
console.log('Start');
|
||||
|
|
@ -77,19 +83,27 @@ setTimeout(() => {
|
|||
}, 0);
|
||||
|
||||
console.log('End');
|
||||
|
||||
// Console output:
|
||||
// Start
|
||||
// End
|
||||
// Promise 1
|
||||
// Timeout 1
|
||||
// Timeout 2
|
||||
```
|
||||
|
||||
### Execution order
|
||||
Explanation of the output:
|
||||
|
||||
1. `Start` and `End` are logged first because they are part of the initial script.
|
||||
2. `Promise 1` is logged next because promises are micro tasks and micro tasks are executed immediately after the current script.
|
||||
3. `Timeout 1` and `Timeout 2` are logged last because they are macro tasks and are processed after the micro tasks.
|
||||
1. `Promise 1` is logged next because promises are microtasks and microtasks are executed immediately after the items on the call stack.
|
||||
1. `Timeout 1` and `Timeout 2` are logged last because they are macrotasks and are processed after the microtasks.
|
||||
|
||||
## Further reading and resources
|
||||
|
||||
- [The event loop - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop)
|
||||
- [The Node.js Event Loop](https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick)
|
||||
- [Event loop: microtasks and macrotasks](https://javascript.info/event-loop)
|
||||
- [JavaScript Visualized - Event Loop, Web APIs, (Micro)task Queue by Lydia Hallie](https://www.youtube.com/watch?v=eiC58R16hb8)
|
||||
- [Philip Robert's talk on the Event Loop](https://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html)
|
||||
- [In The Loop by Jake Archibald](https://www.youtube.com/watch?v=cCOL7MC4Pl0).
|
||||
- [JavaScript Visualized: Event Loop](https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif)
|
||||
- [JavaScript Visualized: Event Loop by Lydia Hallie](https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif)
|
||||
- ["JavaScript Visualized - Event Loop, Web APIs, (Micro)task Queue" by Lydia Hallie](https://www.lydiahallie.com/blog/event-loop)
|
||||
- ["What the heck is the event loop anyway?" by Philip Robert](https://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html)
|
||||
- ["In The Loop" by Jake Archibald](https://www.youtube.com/watch?v=cCOL7MC4Pl0)
|
||||
|
|
|
|||
Loading…
Reference in New Issue