Kad*_*BOT 2 javascript sorting interrupt worker web-worker
我想使用Web Workers对数组进行排序.但是这个数组可能会随着时间的推移接收到新值,而worker仍在执行sort函数.
所以我的问题是,如何在收到新项目后"停止"工作者的排序计算,这样它就可以对该项目执行数组排序,同时仍然保留已经进行的排序?
例:
let worker = new Worker('worker.js');
let list = [10,1,5,2,14,3];
worker.postMessage({ list });
setInterval(() => worker.postMessage({ num: SOME_RANDOM_NUM, list }), 100);
worker.onmessage = event => {
list = event.data.list;
}
Run Code Online (Sandbox Code Playgroud)
让我们说,我已经过了50岁,工人在此之前在排序方面取得了一些进展,现在我有这样的事情:
[1, 2, 3, 10, 5, 14, 50]
.这意味着排序在索引处停止3
.所以我将这个new
数组传递回worker,因此它可以继续从位置进行排序3
.
我怎样才能实现这一点,因为没有办法暂停/恢复网络工作者?
即使Worker工作在主页之外的其他线程上,并且因此可以连续运行而不阻塞UI,它仍然在单个线程上运行.
这意味着在排序算法完成之前,Worker将延迟执行消息事件处理程序; 它与主线程一样被阻止.
即使你从这个工人内部使用了另一个工人,问题也是一样的.
唯一的解决方案是使用一种生成器函数作为分类器,并不时地产生它,以便事件可以执行.
但这样做会大大减慢排序算法的速度.
为了使它更好,您可以尝试挂钩到每个事件循环,这要归功于MessageChannel对象:您在一个端口进行通信并在下一个Event循环中接收消息.如果再次与另一个端口通话,那么每个Event循环都有自己的挂钩.
现在,最好的方法是在每个Event循环中运行一个好的批处理,但是对于demo,我只调用我们的生成器函数的一个实例(我从这个Q/A中借用)
const worker = new Worker(getWorkerURL());
worker.onmessage = draw;
onclick = e => worker.postMessage(0x0000FF/0xFFFFFF); // add a red pixel
// every frame we request the current state from Worker
function requestFrame() {
worker.postMessage('gimme a frame');
requestAnimationFrame(requestFrame);
}
requestFrame();
// drawing part
const ctx = canvas.getContext('2d');
const img = ctx.createImageData(50, 50);
const data = new Uint32Array(img.data.buffer);
ctx.imageSmoothingEnabled = false;
function draw(evt) {
// converts 0&1 to black and white pixels
const list = evt.data;
list.forEach((bool, i) =>
data[i] = (bool * 0xFFFFFF) + 0xFF000000
);
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.putImageData(img,0,0);
// draw bigger
ctx.scale(5,5);
ctx.drawImage(canvas, 0,0);
}
function getWorkerURL() {
const script = document.querySelector('[type="worker-script"]');
const blob = new Blob([script.textContent]);
return URL.createObjectURL(blob);
}
Run Code Online (Sandbox Code Playgroud)
body{
background: ivory;
}
Run Code Online (Sandbox Code Playgroud)
<script type="worker-script">
// our list
const list = Array.from({length: 2500}).map(_=>+(Math.random()>.5));
// our sorter generator
let sorter = bubbleSort(list);
let done = false;
/* inner messaging channel */
const msg_channel = new MessageChannel();
// Hook to every Event loop
msg_channel.port2.onmessage = e => {
// procede next step in sorting algo
// could be a few thousands in a loop
const state = sorter.next();
// while running
if(!state.done) {
msg_channel.port1.postMessage('');
done = false;
}
else {
done = true;
}
}
msg_channel.port1.postMessage("");
/* outer messaging channel (from main) */
self.onmessage = e => {
if(e.data === "gimme a frame") {
self.postMessage(list);
}
else {
list.push(e.data);
if(done) { // restart the sorter
sorter = bubbleSort(list);
msg_channel.port1.postMessage('');
}
}
};
function* bubbleSort(a) { // * is magic
var swapped;
do {
swapped = false;
for (var i = 0; i < a.length - 1; i++) {
if (a[i] > a[i + 1]) {
var temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
swapped = true;
yield swapped; // pause here
}
}
} while (swapped);
}
</script>
<pre> click to add red pixels</pre>
<canvas id="canvas" width="250" height="250"></canvas>
Run Code Online (Sandbox Code Playgroud)