内部的Web工作者承诺导致崩溃

时间:2016-10-16 02:10:05

标签: javascript multithreading google-chrome promise web-worker

我的代码的预期工作流程是从getData获取数据。

getData调用将对输入执行ImageUtil.getHex的工作程序。 ImageUtil.getHex是一个繁重的函数,需要迭代图像区area的每个像素,这就是我想创建它的原因,它在后台运行并在多线程中完成。该功能也是独立的,我认为这是一个很好的候选人。

这是需要getData函数的代码块:

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
            });
        //... 
      }
    }
  // ...
  }

  // ...
}

这是getData函数:

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;
  }
}

问题是,当运行时,它会导致浏览器崩溃。即使它没有崩溃,性能也比不使用worker更慢。

重现的步骤:

  1. 此处未提及的代码的另一部分创建了一个 Mosaic对象。然后,我们在Mosaic对象上调用build()
  2. 崩溃
  3. 我想我误解了工人的工作方式。这是正确的方法,我如何修复代码,使其不再崩溃?

    谢谢!

1 个答案:

答案 0 :(得分:1)

问题是您在嵌套makeTile循环中呼叫for,其中makeTile创建worker。您正在创建950 Worker个实例。每个Worker实例都会调用postMessage。这就是浏览器崩溃的原因。

您需要调整脚本来处理承诺数组,而不是单个Promiseworker.js应该被调用一次,而不是950次。

您可以在for循环之前创建数组,将数据作为数组传递给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]))
    }
  };

然后在for循环后使用Promise.all()来处理数据

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

   })
}))

移动

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

makeTile()之外,或创建逻辑,以便只进行一次调用。

同样,在worker.js,调整脚本以处理数据数组,而不是单个值。

在主线程触发message事件时,将数据作为值数组处理。

解决方案的要点是重构代码库以处理数组和promises数组;在主线程和worker.js;该对象最多只能在worker.js拨打change一次 <input type="file">元素的事件。将message发布到Worker的单个messageWorker预计发出单import { Component } from '@angular/core'; @Component({ selector: 'my-app', template: '<h1>{{title}}</h1><h2>{{hero}} details!</h2>' }) export class AppComponent { title = 'Tour of Heroes'; hero = 'Windstorm'; } 个事件。然后使用包含已处理数据的返回数组执行操作。