javascript - 如何让 Webworker 真正响应以及为什么 setTimeout() 不起作用

标签 javascript multithreading web-worker

我正在研究 Javascript Web Workers。为此,我创建了以下页面和脚本:

worker-test.html

<!DOCTYPE html>
<html>
<head>
    <title>Web Worker Test</title>
    <script src="worker-test.js"></script>
</head>
<body onload="init()">
<form>
    <table width="50%">
        <tr style="text-align: center;">
            <td width="25%">
                <label >Step Time</label>
                <select id="stepTime" onchange="changeStepTime();">
                    <option>500</option>
                    <option>1000</option>
                    <option>2000</option>
                </select>
            </td>
            <td width="25%">
                <button id="btnStart" onclick="return buttonAction('START');">Start</start>
            </td>
            <td width="25%">
                <button id="btnStop" onclick="return buttonAction('STOP');">Stop</start>
            </td>
            <td width="25%">
                <button id="btnClean" onclick="return clean();">Clean</start>
            </td>
        </tr>
        <tr>
            <td colspan="4">
                <textarea id="log" rows="15" style="width: 100%; font-size: xx-large;"  readonly="readonly"></textarea>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

worker-test.js

function id(strId) {
    return document.getElementById(strId);
}

worker = new Worker('worker.js');
worker.onmessage =  function(e) {
    switch(e.data.msg) {
        case "STARTED":
            console.info(e.data);
            id("btnStart").disabled = true;
            id("btnStop").disabled = false;
            break;
        case "STOPPED":
            console.info(e.data);
            id("btnStart").disabled = false;
            id("btnStop").disabled = true;
            break;
        case "TIME":
            id("log").value += e.data.time + '\n';
            break
        default:
            // ...
    }
};

function buttonAction(action) {
    worker.postMessage({msg: action}); 
    return false;
}

function changeStepTime() {
    worker.postMessage({msg: "STEPTIME", stepTime: id("stepTime").value});
}

function clean() {
    id('log').value = ''; 
    return false;
}

function init() {
    id("btnStop").disabled = true;
    changeStepTime();
}

worker.js

var running = false;
var stepTime = 0;

// Replacement of setTimeout().
function pause() {
    var t0 = Date.now();
    while((Date.now() - t0) < stepTime) {
        (function() {}) ();
    }
}

function run() {
    while(running) {
        postMessage({msg: "TIME", time: new Date().toISOString()});
        // pause();
        setTimeout(function() {console.debug("PAUSE!!!")}, stepTime);
    }
}

addEventListener('message', function(e) {
    switch(e.data.msg) {
        case "START":
            running = true;
            console.debug("[WORKER] STARTING. running = " + running);
            run();
            postMessage({msg: "STARTED"});
            break;
        case "STOP":
            running = false;
            console.debug("[WORKER] STOPPING. running = " + running);
            postMessage({msg: "STOPPED"});
            break;
        case "STEPTIME":
            stepTime = parseInt(e.data.stepTime);
            console.debug("[WORKER] STEP TIME = " + stepTime);
            postMessage({msg: "STEP TIME CHANGED"});
            break;
        default:
            console.warn("[WORKER] " + e.data);
    }
});

页面应该如何做:当我单击开始按钮时,它会向调用运行函数的工作人员发送一条消息。在这个函数中有一个 while 循环。在那里,首先向 UI 线程发送一条消息,其中当前时间显示在 <TEXTAREA> 中。 。接下来,setTimeout()被调用以暂停stepTime设置的时间<SELECT>元素。另外,当循环开始时,必须禁用“开始”按钮并启用“停止”按钮。当单击停止时,它会更改 running将 Worker 中的标记设置为 false 并停止循环。此外,必须启用开始按钮并禁用停止按钮。总是当我单击按钮时,控制台会显示发送给工作人员的消息,然后显示 UI 线程从工作人员接收的消息。当我更改stepTime<SELECT>值,工作线程暂停时间发生变化。 清理按钮只是清理 <TEXTAREA>

我真正得到的:当我点击开始时,<TEXTAREA>充满了当前时间消息,并且控件根本不响应。实际上,所有浏览器实例(我在 Firefox 中测试)都会卡住并在一段时间后崩溃。显然,setTimeout()虽然它在 UI 中工作,但在 Worker 中不起作用(例如,在控制台中我输入 setTimeout(function() {console.debug("PAUSE!!!")}, id("stepTime").value); )。

我尝试的解决方法是替换 setTimeout()调用 pause()功能。我得到这个:页面(和浏览器)没有卡住。 <TEXTAREA>显示时间消息,间隔最初在 <SELECT> 中设置。 。 清洁按钮按预期工作。但是,当循环启动时,它会在工作线程 ( console.debug("[WORKER] STARTING. running = " + running); ) 中显示控制台消息,但不显示工作线程响应 ( postMessage({msg: "STARTED"}); )。 开始 均未禁用,停止 均未启用。此外,如果我更改 <SELECT>值,它不会改变间隔。如果我确实想更改间隔,我需要重新加载页面并设置另一个值。

查看代码,很明显为什么当我启动循环时没有调用响应,因为 run()函数是无限循环,因此永远不会到达响应命令。

因此,我有两个问题:

  1. 如何更改代码以使其真正异步并且 react 灵敏。
  2. 为什么 setTimeout()如果它应该根据 Web Worker definition 工作,则不工作.

谢谢

拉斐尔·阿方索

最佳答案

1)对于第一个问题: 这段代码:

function run() {
    while(running) {
        postMessage({msg: "TIME", time: new Date().toISOString()});
        // pause();
        setTimeout(function() {console.debug("PAUSE!!!")}, stepTime);
    }
}

将会挂起你的浏览器,因为它会postMessage与你的CPU运行一样快,因为你处于一种while(true)状态,这会卡住你的网络 worker 。 pause 函数也会做同样的事情,基本上是一段时间(true)。 要使此代码异步运行,您应该控制何时在 WebWorker 内部调用 postMessage,例如每完成作业 10% 一次……而不是在一段时间内为 true。

2)对于第二个问题: while 内的这行代码

setTimeout(function() {console.debug("PAUSE!!!")}, stepTime);

不会暂停它,它不会影响循环。在javascript中没有sleep类似 php 的方法。

要达到此效果,您需要将 run 函数替换为以下函数:

function run() {
    if(running){
        postMessage({msg: "TIME", time: new Date().toISOString()});
        setTimeout(function() { run(); }, stepTime);
    }
}

关于javascript - 如何让 Webworker 真正响应以及为什么 setTimeout() 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29234495/

相关文章:

javascript - 为什么在我的测试中使用 .then() 比 await 快 3 倍?

javascript - ASP.NET 中文本框的按键事件

javascript - "classic"和 "module"Web Worker 有什么区别?

web - Service Worker 可以拦截来自 Web Worker 的 http 请求吗?

javascript - 如何删除或改进 :focus style on Bootstrap Carousel?

android - 播放 mp3 Android 异步。播放但随后崩溃线程

c++ - 在 C++ 中使用多核处理大型顺序文件

c# - 实现多线程后与数据库的连接丢失

javascript - Web Workers 中的 Performance.now() 可靠吗?

javascript - 如何防止更改复选框的父单击事件