Web workers inside promise causing crash

醉酒当歌 提交于 2019-12-21 06:06:17

问题


The expected workflow of my code is getting the data from getData.

getData calls the worker that will do ImageUtil.getHex on the input. ImageUtil.getHex is a heavy function that needs to iterate every pixel of an image area area, so that's why I want to create it runs in the background and done in multithreading. The function is also independent, which I guess is a good candidate to put in the worker.

This is the chunk of code that needs the getData function:

class Mosaic {
    // ...
  build() {
    for (let y = 0; y < canvas.height; y += options.tileHeight) {
      for (let x = 0; x < canvas.width; x += options.tileWidth) {
        //... some code here
        // imgContext: a context of a canvas containing the image
        let area = imgContext.getImageData(x, y, w, h);
        this.getData(area, x, y)
          .then((data) => {
              // do something with data
            });
        //... 
      }
    }
  // ...
  }

  // ...
}

this is the getData function:

getData(data, x, y) {
  return new Promise((resolve, reject) => {
    let worker = new Worker('js/worker.js');
    worker.onmessage = (e) => {
      let hex = e.data;
      let img = new Image();
      let loc = `image/${hex}`
      img.onload = (e) => {
        resolve({
          hex: hex,
          x: x,
          y: y
        });
      }
      img.src = loc;
    }
    worker.postMessage(data);
  }); 

js/worker.js

self.addEventListener('message', function(e) {
  let hex = ImageUtil.getHex(e.data); // another function
  self.postMessage(hex);
  self.close();
}, false);

class ImageUtil {
  static getHex(imgData) {
    let data = imgData.data;
    let r = 0,
        g = 0,
        b = 0,

    for (let i = 0; i < data.length; i += 4) {
      // count rgb here
    }

    let count = data.length / 4;
    r = ("0" + (Math.floor(r / count)).toString(16)).slice(-2);
    g = ("0" + (Math.floor(g / count)).toString(16)).slice(-2);
    b = ("0" + (Math.floor(b / count)).toString(16)).slice(-2);
    let hex = `${r}${g}${b}`;
    return hex;
  }
}

The problem is, when ran, it makes the browser crash. Even if it's not crashing, the performance is much slower than without using worker.

Steps to reproduce:

  1. The other part of the code that isn't mentioned here create a Mosaic object. Then, we call build() on that Mosaic object.
  2. Crash

I think I misunderstood the way of workers work. Is it the right way, and how do I fix the code so it won't crash anymore?

Thanks!


回答1:


The issue is that you are calling makeTile within a nested for loop, where makeTile creates worker. You are creating 950 Worker instances. Each Worker instance calls postMessage. That is the reason the browser is crashing.

You need to adjust you scripts to handle arrays of promises, instead of a single Promise. worker.js should be called once, not 950 times.

You can create an array before the for loops, pass the data as an array to Promise.resolve()

  var arr = [];
  for (let y = 0; y < canvas.height; y += options.tileHeight) {
    for (let x = 0; x < canvas.width; x += options.tileWidth) {
      let areaData = imgContext
                     .getImageData(x, y, options.tileWidth, options.tileHeight);
      arr.push(Promise.resolve([areaData, x, y]))
    }
  };

then after the for loops use Promise.all() to process the data

Promise.all(arr.map(function(tile) {
   this.makeTile(/* tile data here */) // make necessary changes at `makeTile`
   .then(function(tiles) {
     // do stuff with `tiles` array

   })
}))

Move

let worker = new Worker('worker.js');

outside of makeTile(), or create logic so that the call is only made once.

Similarly, at worker.js, adjust the script to handle an array of data, instead of a single value.

When message event is fired at main thread, process the data as an array of values.

The gist of the solution is to refactor your code base to handle arrays, and arrays of promises; both at main thread and at worker.js; with the object being to call worker.js at most once at change event of <input type="file"> element. Where a single message is posted to Worker, and single message event is expected from Worker. Then do stuff with the returned array containing the processed data.



来源:https://stackoverflow.com/questions/40066129/web-workers-inside-promise-causing-crash

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!