Saturday, August 24, 2024

JavaScript Blobs

A Blob (Binary Large Object) is a data structure used to store raw data. It can be created using the Blob constructor. For instance:

const myBlob = new Blob(['Hello, world!'], { type: 'text/plain' });

You can use Blobs to create URLs, which can be directly embedded into HTML documents. For example, you can create a Blob containing text data and then generate a download link for it. When the user clicks this link, they can download the Blob content as a file. This is shown below:

<html>
<head/>
<body>
  <h1>Download Blob Example</h1>

  <script>
    const createDownloadLink = (content, filename) => {
      // Create a Blob from the content
      const blob = new Blob([content], { type: 'text/plain' });
      
      // Create a URL for the Blob
      const url = URL.createObjectURL(blob);
      
      // Create an <a> element
      const a = document.createElement('a');
      a.href = url;
      a.download = filename;
      a.textContent = `Download ${filename}`;

      // append to body
      document.body.appendChild(a);
      
      // revoke URL after some time or on user action
      // URL.revokeObjectURL(url); 
    }
    createDownloadLink('some content', 'example.txt');
  </script>
</body>
</html>

You can also use a Blob to dynamically generate code and create JavaScript files on-the-fly! Here’s an example of how to create a Web Worker from a Blob:

<html>
<head/>
<body>
  <h1>Web Worker Blob Example</h1>
  <p id="result"></p>

  <script>
    const workerScript = `
      onmessage = e => {
        postMessage(e.data * 2);
      };
    `;

    const blob = new Blob([workerScript], { type: 'application/javascript' });
    const url = URL.createObjectURL(blob);
    const worker = new Worker(url);

    worker.onmessage = e => {
      document.getElementById('result').textContent = 'Worker result: ' + e.data;
      URL.revokeObjectURL(url); // Clean up Blob URL
    };

    worker.postMessage('2');
  </script>
</body>
</html>

Saturday, August 10, 2024

Shared Web Workers

In my previous post, I discussed how Web Workers can be used to enhance the responsiveness of web applications by offloading resource-intensive computations to run in the background, thus preventing them from blocking the main thread. Today, let's look into Shared Web Workers and how they can further boost your app's efficiency.

Shared Web Workers are a special type of Web Worker that can be accessed from multiple browsing contexts, such as different tabs, windows, or iframes. This shared access can facilitate various functionalities, including real-time communication, managing shared state, and caching data across multiple tabs or windows. By reusing a single worker instance, Shared Web Workers help reduce memory consumption and improve performance compared to creating a new worker for each context.

The following example shows the basics of using a Shared Web Worker.

1. Create the Shared Web Worker Script

Shared Web Workers use ports to communicate. You need to handle the onconnect event to establish communication with the port and the onmessage event to process incoming messages.

// worker.js
onconnect = (event) => {
  const port = event.ports[0];
  port.onmessage = (e) => {
    port.postMessage(e.data[0] + e.data[1]);
  };
};

2. Use the Shared Web Worker in Your Main Script

In your main script, initialise the Shared Web Worker. This can be done from multiple scripts or HTML pages. Once created, any script running on the same origin can access the worker and communicate with it. The various scripts will use the same worker for tasks, even if they are running in different windows.

<html>
  <head/>
  <body>
    <h1>Shared Web Worker Example</h1>
    <p id="result">Computing...</p>
    <script>
      const worker = new SharedWorker('path/to/worker.js');
      worker.port.onmessage = (e) => {
        document.getElementById('result').textContent = `Result: ${e.data}`;
      };
      worker.port.postMessage([1, 2]);
    </script>
  </body>
</html>

Communication between the main script and the Shared Web Worker is done using the port.postMessage method to send messages and the port.onmessage event to receive messages.

Related posts:
Web Workers

Saturday, August 03, 2024

Web Workers

Web Workers allow you to perform resource-intensive computations in background threads, without blocking the main thread that handles user interactions and UI updates. This makes it possible to perform tasks such as data processing, complex calculations, and large data fetching asynchronously, keeping your web application responsive.

The following example shows the basics of using a web worker to perform a simple "sum" calculation.

1. Create a Web Worker Script

First, create your web worker script:

// worker.js
onmessage = (e) => {
  const workerResult = e.data[0] + e.data[1];
  postMessage(workerResult);
};

As shown above, the web worker performs the computation in the onmessage event handler and then calls postMessage, to post the result back to the main thread.

2. Invoke the Web Worker in Your Main Script

In the main script, initialise the web worker and invoke it with some data:

<html>
  <head/>
  <body>
    <h1>Web Worker Example</h1>
    <p id="result">Computing...</p>
    <script>
      const worker = new Worker('worker.js');
      worker.onmessage = (e) => {
        document.getElementById('result').textContent = `Result: ${e.data}`;
      };
      worker.postMessage([1, 2]);
    </script>
  </body>
</html>

Communication between the main script and the web worker is done using the postMessage method to send messages and the onmessage event to receive messages.

React Example

Here is how you would do it in React:

import React, { useState } from 'react';

const App = () => {
  const [result, setResult] = useState(null);

  const handleClick = () => {
    // Create a new Web Worker
    const worker = new Worker(new URL('./worker.js', import.meta.url));

    // Set up message handler
    worker.onmessage = (e) => {
      setResult(e.data);
      worker.terminate(); // Clean up the worker
    };

    // send data to the worker
    worker.postMessage([1, 2]);
  };

  return (
    <div>
      <h1>Simple Web Worker Example</h1>
      <button onClick={handleClick}>Start Computation</button>
      {result !== null && <p>Result from Worker: {result}</p>}
    </div>
  );
};
export default App;