"Error occurs when passing data back to main thread from a web worker: undefined data received

Hello, I’ve been experimenting with using a web worker to retrieve data and send it back to the main thread. However, I've encountered an issue with my code not working as expected.

onmessage = (e) => {
  console.log(e);
  if( e.data[0] === 'fetchData' ){
    fetch('https://example.com/platform/api/v1/endpoint')
    .then( (res) => res.json() )
    .then( async (data) => {
      const imagesData = await Promise.all(
        data.map( async (item) => {
          let res = await fetch(item.src);
          let img = await res.blob();
          let reader = new FileReader();
          reader.readAsDataURL(img);
          reader.onloadend = () => { 
            return { 
              title: item.title, 
              link: item.link, 
              src: reader.result 
            }
          }
        })
      )
      postMessage(imagesData);
    })
  }
}

After logging the imagesData, I noticed that it contains seven undefined elements. How can I fix this issue?

UPDATE

I have updated both the Vue front-end code and the web worker logic. However, sometimes the worker fails to function properly or returns only two entries instead of the expected seven items for the front-end. Could this be due to not terminating one work before starting another? Please note, I am developing a Chrome extension for tab overriding.

Vue Front-End Code:

<script>
const worker = new Worker('services.js');

export default {
  name: 'App',
  beforeCreate() {
    worker.postMessage(['fetchData']);
  },
  created() {
    this.init();
    this.clock();
  },
  data() {
    return {
      mostVisited: [],
      imagesData: [],
      isLoading: true 
    }
  },
  methods: {
    init() {
      worker.onmessage = (e) => {
        console.log(e);
        this.imagesData = e.data; 
        this.isLoading = false; 
      }
      browser.topSites.get().then( (sites) => this.mostVisited = sites ); 
    } //end init
  }
</script>

Web Worker Code:

onmessage = (e) => {
  console.log(e);
  if( e.data[0] === 'fetchData' ){
    fetch('https://example.com/platform/api/v1/endpoint')
    .then( (res) => res.json() )
    .then( async (data) => {
      let imagesData = [];
      await Promise.all(
        data.map( async (item) => {
          let res = await fetch(item.src);
          let img = await res.blob();
          let reader = new FileReader();
          reader.readAsDataURL(img);
          reader.onloadend = () => { 
            imagesData.push({ title: item.title, link: item.link, src: reader.result });
          }
        })
      )
      postMessage(imagesData);
    }); // end then(data)
  }
}

Answer №1

The outer Promise resolves before the asynchronous FileReader finishes reading the files, causing the Promise.all Promise to resolve after let img = await res.blob();, but before onloadend.

In a Worker context, you can utilize the synchronous FileReaderSync API like this:

.then( async (data) => {
  let imagesData = [];
  await Promise.all(
    data.map( async (item) => {
      let res = await fetch(item.src);
      let img = await res.blob();
      let reader = new FileReaderSync();
      const result = reader.readAsDataURL(img);
      imagesData.push({ title: item.title, link: item.link, src: result });
    })
  )
  postMessage(imagesData);
});

However, it is likely that the data: URL is unnecessary and may even cause more harm than good.

When using a data: URL from a FileReader, the data is encoded to base64 which increases its size by 134%, then further increased due to UTF-16 storing DOMStrings. This inflated data needs to be serialized using the structured clone algorithm, resulting in images consuming about 5 times their original size in memory before being processed again by the main context for pixel data...

Instead of passing the data: URL, simply pass the Blob obtained from img. The inner data of Blobs is passed by reference, minimizing the memory consumption. When displaying these images in the main context, use URL.createObejctURL(img) to create a blob: URL directly pointing to the Blob's data stored once in memory.

.then( async (data) => {
  let imagesData = [];
  await Promise.all(
    data.map( async (item) => {
      let res = await fetch(item.src);
      let img = await res.blob();
      const url = URL.createObjectURL(img);
      imagesData.push({ title: item.title, link: item.link, src: url, file: img });
    })
  )
  postMessage(imagesData);
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to retrieve the id of a Vue component programmatically in Vue2?

Hey everyone, I have a scenario where I'm working with a VueComponent and I need to pass it to $vuetify.goTo() in order to scroll the page to that specific component. Sadly, while $vuetify.goto() supposedly accepts a VueComponent, it actually requires ...

Utilizing document.write() for displaying markup content

I have an inline SVG stored as a variable on my webpage and I am making some changes to it. How can I display viewText on the page (not the SVG image) with the modifications? What is the best way to make viewText appear on the page? For instance: ...

Is there a way to initiate a callback function once all the contents of an UpdatePanel have been fully loaded?

Looking for a solution with an ASP.NET UpdatePanel that contains multiple images. I am trying to trigger some javascript code after the UpdatePanel is refreshed, but only after all images have finished loading. I attempted using add_endRequest as a callb ...

What could be the reason for the absence of the loading sign in Chrome, even though it appears when the code is run on Firefox?

I implemented a function to display a loading screen on my HTML page with Google Maps integration. However, when I called the function popUpLoadingScreen() and dismissLoadingScreen() to show and hide the loading message while rendering map markers, the loa ...

The rating system does not accurately incorporate the values provided by the API

Incorporated the star rating package into my ReactJS code to showcase the star value retrieved from a mock API. import { Rating } from "react-simple-star-rating"; However, when attempting to make it read-only, it does become static but fails to ...

Retrieving video information using Dailymotion API with JSON and jQuery

I have been struggling to understand the issue even after consulting the Dailymotion API and various sources. I am attempting to retrieve data from Dailymotion for a specific video ID by using the following code: $.getJSON('https://api.dailymotion.co ...

Retrieving User Activity Reports for a specified set of users within G Suite

I am currently attempting to utilize the Admin SDK Reports Service to retrieve the latest login time and other data for a specific set of 20 users. Due to the large size of the domain, it is not practical to fetch data for the entire domain and then filter ...

What is the best way to ensure a div occupies the full height within a grid layout?

https://i.sstatic.net/awwxE.png Looking at the image provided, I am seeking a way to make the top left image occupy the entire height just like the neighboring div in the first row of the grid. Is there a way to achieve this? The desired outcome should b ...

I am being thrown an npm ERR! by Heroku because it says there is a missing script called "start"

I'm facing issues while trying to deploy a simple app on Heroku. The application keeps crashing and these errors keep popping up. In my setup, I have included: "start": "node app.js", The Procfile also contains the command &a ...

Outputting PHP code as plain text with jQuery

My aim is to set up a preview HTML section where I am encountering a difficulty. I am struggling to display PHP code when retrieving and printing it from a textarea in the HTML. Here are my current codes, This is the HTML area where the textarea code will ...

Exploring ways to utilize removeEventListener in React Native

Can someone assist me with converting this code to React? I am new to using React and struggling with the implementation. Thank you in advance! <progress id="bar" value="0" max="110"></progress> <button onClick={i ...

Troubleshoot my code for clustering markers on a Google map

I'm currently working on a piece of code to generate a Google map that contains 3 hidden points within one marker. The idea is that when the main marker is clicked, these points will either merge into one or expand into 3 separate markers. However, I& ...

Can a Javascript file be concealed from view?

Imagine a scenario where the localhost root file is served an HTML file using the following code: app.get('/', (req, res) => res.sendfile('index.html')) Is it possible to include JavaScript files in the HTML document that are not a ...

Efficiently manage and switch between multiple VueJS applications on a single page without any redirection needed

In the process of developing an extensive project that consists of multiple VueJS applications, each with its own router and store. Access to these applications is through URLs like: https://baseurl.com/app1/#/ https://baseurl.com/app2/#/ https://baseurl ...

List output with jQuery AJAX showing object data

Here is my code that utilizes ajax for searching: $("#keyword").keyup(function() { var keyword = $("#keyword").val(); if (keyword.length >= MIN_LENGTH) { $.get( "./lib/data_siswa_ajax.php", { keyword: keyword, sekolah: $("#sekolah").val ...

Ordering an array in a VUEjs template using computed properties

Working with a VUE template, I retrieve an array from an API which contains a list of countries. Now, based on the ID received, I have a requirement to rearrange this array... The code snippet may resemble something like: Vue.component('select-list ...

The submitHandler() function in the jQuery validate method is experiencing delays when executing and processing the form submission

Currently, I am using the jQuery validate method to validate my form. I have implemented some code in the submitHandler() method, but it seems to be taking longer than expected to execute. Can anyone provide me with a solution to resolve this issue? $(&ap ...

Pattern matching algorithm designed to eliminate background-color attributes

Looking to strip out any "background-color:[whatever];" styles from the text within a div. The plan is to eliminate all inline background-color styles completely. I've been eyeing JavaScript's string.replace(regex,str) as a potential solution ...

Encountered a problem when trying to import the function "createToken" into a Node.js middleware

I have developed a model called users in which I included a method named generateToken for generating web tokens. This model is being used with the Sequelize ORM. module.exports = (sequelize, Sequelize) => { const Tutorial = sequelize.define("u ...

What steps do I need to follow in order to properly execute this HTTP request?

Recently, I came across this amazing tool called SimplePush.io that is perfect for one of my projects. It works flawlessly via curl, as shown on their website: ~ $ curl 'https://api.simplepush.io/send/HuxgBB/Wow/So easy' or ~ $ curl --data &ap ...