我的代码的预期工作流程是从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更慢。
重现的步骤:
Mosaic
对象。然后,我们在Mosaic对象上调用build()
。我想我误解了工人的工作方式。这是正确的方法,我如何修复代码,使其不再崩溃?
谢谢!
答案 0 :(得分:1)
问题是您在嵌套makeTile
循环中呼叫for
,其中makeTile
创建worker
。您正在创建950 Worker
个实例。每个Worker
实例都会调用postMessage
。这就是浏览器崩溃的原因。
您需要调整脚本来处理承诺数组,而不是单个Promise
。 worker.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
的单个message
,Worker
预计发出单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';
}
个事件。然后使用包含已处理数据的返回数组执行操作。