如何让Web Workers在执行计算的同时接收新数据?

2024-06-18

我想使用 Web Workers 对数组进行排序。但随着时间的推移,该数组可能会收到新值,而工作线程仍在执行排序功能。

所以我的问题是,在收到新项目后,如何“停止”工作人员的排序计算,以便它可以对该项目的数组执行排序,同时仍然保持已经进行的排序?

Example:

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

假设我已经过了 50 岁,工作人员在此之前在排序方面取得了一些进展,现在我得到了这样的结果:[1, 2, 3, 10, 5, 14, 50]。这意味着排序在索引处停止3。所以我通过这个new数组返回给worker,这样它就可以从位置继续排序3.

由于无法暂停/恢复网络工作人员,我该如何实现这一点?


尽管 Worker 在主页面之外的其他线程上工作,因此可以连续运行而不会阻塞 UI,但它仍然在单个线程上运行。

这意味着,在您的排序算法完成之前,Worker 将延迟消息事件处理程序的执行;它和主线程一样被阻塞。

即使您从该 Worker 内部使用其他 Worker,问题也是一样的。

唯一的解决方案是使用一种生成器函数 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*作为排序器,并时不时地产生它,以便事件可以被执行。

但这样做会大大减慢排序算法的速度。

为了使它更好,您可以尝试挂钩每个事件循环,这要归功于 MessageChannel 对象:您在一个端口中通话并在下一个事件循环中接收消息。如果您再次与另一个端口通信,那么您对每个事件循环都有自己的挂钩。

现在,最好的办法是在每个事件循环中运行一批好的批处理,但对于演示,我将仅调用生成器函数的一个实例(我从this Q/A https://stackoverflow.com/q/46951400/3702797)

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);
}
body{
  background: ivory;
}
<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>

请注意,使用异步函数也可以实现相同的目的,这在某些情况下可能更实用:

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);
}
body{
  background: ivory;
}
<script type="worker-script">
// our list
const list = Array.from({length: 2500}).map(_=>+(Math.random()>.5));
// our sorter generator
let done = false;


/* 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
      bubbleSort(list);
    }
  }
};

async function bubbleSort(a) { // async is magic
  var swapped;
  do {
    swapped = false;
    for (var i = 0; i < a.length - 1; i++) {
      if (a[i] > a[i + 1]) {
        const temp = a[i];
        a[i] = a[i + 1];
        a[i + 1] = temp;
        swapped = true;
      }
      if( i % 50 === 0 ) { // by batches of 50?
        await waitNextTask(); // pause here
      }
    }
  } while (swapped);
  done = true;
}

function waitNextTask() {
  return new Promise( (resolve) => {
    const channel = waitNextTask.channel ||= new MessageChannel();
    channel.port1.addEventListener("message", (evt) => resolve(), { once: true });
    channel.port2.postMessage("");
    channel.port1.start();
  });
}

bubbleSort(list);
</script>
<pre> click to add red pixels</pre>
<canvas id="canvas" width="250" height="250"></canvas>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何让Web Workers在执行计算的同时接收新数据? 的相关文章

随机推荐

  • OracleDataSource.getConnection 是线程安全的吗?

    我以为找到这个答案很容易 但并不是那么容易 有谁知道 OracleDataSource getConnection 方法是否是线程安全的 我不是指它返回的 Connection 对象 而是指 getConnection 本身的调用 具体来说
  • Xamarin.Forms 按钮的内容

    我正在尝试将自定义内容添加到 Xamarin Forms 中的按钮 默认情况下 按钮是这样创建的
  • 将“Authorization Bearer”哈希添加到 Net::HTTP post 请求 (Ruby)

    我怎样才能添加Authorization Bearer到 POST 请求Net HTTP 我只能在文档中找到 基本身份验证 的帮助 req basic auth user pass Source https docs ruby lang o
  • 当实际端点关闭时,如何避免 wso2 esb 中的端点挂起

    我正在使用消息存储来存储消息 以防实际端点出现故障 我的代理使用 vfs 传输 即它将从文件中读取消息并传送到端点 如果端点出现故障 它会将消息存储在配置的 activemq 中 当我保留单个文件时 我的配置工作正常 在保留更多文件 即更多
  • requestAnimationFrame 垃圾回收

    我正在使用 Chrome 开发工具 v27 中的时间轴分析以下代码的内存使用情况
  • 我可以使用passport-google回调来验证android/ios用户吗?

    我有一个使用 google passport oauth2 进行身份验证的 node js 服务器 我的服务器端代码看起来像这样文档 https www npmjs com package passport google oauth2 au
  • 在 Qt 中,许多插槽连接到同一信号,它们在发出信号时是否按顺序调用?

    In the Qt文件说 如果多个插槽连接到一个信号 则这些插槽将 按照它们连接的顺序一个接一个地执行 当信号发出时 但在connect 功能 设置Qt ConnectionType输入为Qt QueuedConnection意思是 当控制
  • 如何将动画应用到 GMSMarker

    我正在通过使用适用于 iOS V1 1 0 的 Google Maps SDK 将 iOS 地图迁移到 google 地图来更改我的应用程序 并且我尝试在添加 删除时对标记进行动画处理 但我在与此相关的文档中没有找到任何建议 请建议我如何在
  • HTML/CSS - 打印样式、背景颜色和图像不会在 IE 和 Firefox 中显示?

    我如何在打印时显示背景颜色和图像 我知道这是浏览器属性 但我想从 CSS 完成它 例如我用于 webkit 的 CSS webkit print color adjust exact 那么我怎样才能实现它呢 不 这是不可能的 请参阅 web
  • cpp 浅复制或深复制中的默认复制构造函数?

    C 中默认的复制构造函数是进行浅复制还是深复制 我真的对 cpp 中的默认复制构造函数感到困惑 因为它执行浅复制或深复制 就像我所做的那样v2 v1 认为v1 1 2 3 现在如果我已经完成了v2 0 1 它没有得到反映 但我听说它做了浅复
  • global.asax 范围和生命周期澄清

    我需要在我当前正在进行的项目中实现多个应用程序级行为 有几件事我需要弄清楚 1 在哪里以及如何定义应用程序级变量 2 这些变量的生命周期是多长 或者更准确地说 在什么情况下它们会被丢弃 应用程序池回收 应用程序二进制文件从内存中删除并在下一
  • Valgrind 缺失错误

    原帖是here https askubuntu com questions 766482 valgrind missing error 考虑以下明显有错误的程序 include
  • 如何在XAMPP服务器中实现类似路由器登录的用户认证?

    我正在尝试在我的 XAMPP 服务器中设置用户身份验证 类似于路由器在调用特定路由时提示输入用户名和密码的方式 我想了解底层协议以及浏览器如何识别服务器需要身份验证 谁能指导我完成在 XAMPP 中创建此身份验证协议的过程 我希望能够解释所
  • Angular JS $location.path(...) 不触发路由控制器

    所以我尝试使用更新表单提交上的路径 location path search 但它没有触发注册的路线 search 我也尝试过使用尾部斜杠 没办法 我也试过了 scope apply但我刚刚得到 apply already in progr
  • ColdFusion 的最佳 IDE/编辑器? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • JavaFX 脚本和 Java

    简单的问题 我可以在桌面应用程序中混合 Java 和 JavaFX Script 代码吗 如果可能的话 您能给我提供一些示例链接吗 或者我可以将定制的 javafx CustomNode s 打包在 jar 中 并在我的项目中与标准 SWI
  • rvest如何通过id选择特定的css节点

    我正在尝试使用 rvest 包从网页中抓取数据 简单来说 html 代码如下所示 div class style div
  • 谷歌地图在网络应用程序中离线

    如何下载谷歌地图中的所有图像以在网络应用程序中永久使用离线访问 我可以使用以下命令执行在线操作 src maps googleapis com maps api js sensor false language en 但我需要通过在线下载所
  • 用户 ASP.NET 运行于

    我有点困惑 ASP NET 如何在什么用户下运行 在 iis 7 5 和 6 0 上 一些我不清楚的用户帐户是 网络服务 IUSR AppPool 对不同用户的任何澄清以及安全考虑等都将不胜感激 总之 IIS 5 1 Windows XP
  • 如何让Web Workers在执行计算的同时接收新数据?

    我想使用 Web Workers 对数组进行排序 但随着时间的推移 该数组可能会收到新值 而工作线程仍在执行排序功能 所以我的问题是 在收到新项目后 如何 停止 工作人员的排序计算 以便它可以对该项目的数组执行排序 同时仍然保持已经进行的排