Home  >  Article  >  Web Front-end  >  Start with a Web Worker

Start with a Web Worker

WBOY
WBOYOriginal
2023-09-04 12:49:011104browse

One of the many design goals of the JavaScript language was to remain single-threaded and simple. Although I must admit, given the nature of language structures, it's anything but simple! But what we mean by "single-threaded" is that there is only one thread of control in JavaScript; yes, unfortunately, your JavaScript engine can only do one thing at a time.

Now, doesn't this sound too restrictive for using the idle multi-core processors on your computer? HTML5 promises to change all that.


Single-threaded model of JavaScript

Web Workers live in a restricted world without access to the DOM because the DOM is not thread-safe.

One school of thought sees JavaScript's single-threaded nature as a simplification, but another school sees it as a limitation. The latter group has a very good point, especially as modern web applications make heavy use of JavaScript to handle UI events, query or poll server-side APIs, process large amounts of data, and manipulate the DOM based on server responses. p>

Being able to perform so many operations in a single thread of control while maintaining a responsive UI is often a difficult task, forcing developers to resort to hacks and workarounds (such as using setTimeout() , setInterval(), or use XMLHttpRequest and DOM events) to achieve concurrency. However, it's worth noting that these technologies certainly provide a way to call asynchronously, but non-blocking does not necessarily mean concurrent. John Resig explains on his blog why you can't run anything in parallel.

limitation

If you've been using JavaScript for any length of time, you've most likely encountered the following annoying dialog box stating that certain scripts are taking too long to execute. Yes, almost every time your page stops responding, the cause can be attributed to some JavaScript code.

从 Web Worker 开始

The following are some reasons why the browser may hang when executing scripts:

  • Excessive DOM operations: DOM operations are probably the most expensive operations in JavaScript. Therefore, the large number of DOM manipulation operations makes your script a good candidate for refactoring.
  • Never-Ending Loops: It never hurts to scan your code for complex nested loops. These tend to do more work than is actually needed. Maybe you can find a different solution that provides the same functionality.
  • Combining the two: The worst thing we can do is repeatedly update the DOM in a loop when a more elegant solution exists (such as using a DocumentFragment).

Network workers come to the rescue

...Non-blocking does not necessarily mean concurrency...

Thanks to HTML5 and Web Workers, you can now spawn a new thread - providing true asynchrony. The new worker thread can run in the background while the main thread handles UI events, even if the worker thread is busy processing large amounts of data. For example, workers can process large JSON structures to extract valuable information for display in the UI. But enough of my rambling; let's look at some actual code.

Create staff

Typically, the code related to the Web Worker resides in a separate JavaScript file. The parent thread creates a new Worker by specifying the URI of the script file in the Worker constructor, which loads and executes the JavaScript file asynchronously.

var primeWorker = new Worker('prime.js');

Start a worker thread

To start a worker thread, the parent thread sends a message to the worker thread as follows:

var current = $('#prime').attr('value');
primeWorker.postMessage(current);

Parent pages can communicate with workers using the postMessage API, which is also used for cross-origin messaging. In addition to sending primitive data types to workers, the postMessage API also supports passing JSON structures. However, you cannot pass functions as they may contain references to the underlying DOM.

The parent thread and the worker thread have their own independent space; messages passed back and forth are copied, not shared.

Behind the scenes, these messages are serialized at the worker thread and then deserialized at the receiving end. Therefore, sending large amounts of data to worker threads is discouraged.

The parent thread can also register a callback to listen for any messages sent back by the worker thread after performing its tasks. This allows the parent thread to take necessary actions (such as updating the DOM) after the worker thread has done its job. Take a look at this code:

primeWorker.addEventListener('message', function(event){
    console.log('Receiving from Worker: '+event.data);
    $('#prime').html( event.data );
});

event The object contains two important attributes:

  • <b>target</b>: Used to identify the worker sending the message; mainly useful in multi-worker environments.
  • <b>data</b>: The message the worker sends back to its parent thread.
The

worker itself is contained in prime.js and registers for message events received from its parent. It also uses the same postMessage API to communicate with the parent thread.

self.addEventListener('message',  function(event){
    var currPrime = event.data, nextPrime;
    setInterval( function(){

    nextPrime = getNextPrime(currPrime);
    postMessage(nextPrime);	
    currPrime = nextPrime;

    }, 500);
});

网络工作者生活在一个受限且线程安全的环境中。

在此示例中,我们只需找到下一个最大素数,然后重复将结果发送回父线程,父线程又用新值更新 UI。在工作者上下文中, selfthis 均指全局范围。 Worker 可以为 message 事件添加事件侦听器,也可以定义 onmessage 处理程序来侦听父线程发送的任何消息。

查找下一个素数的任务显然不是工作人员的理想用例,但在这里选择它是为了演示传递消息的概念。随后,我们确实探索了使用 Web Worker 真正能带来好处的可能且实际的用例。

终止员工

工人是资源密集型的;它们是操作系统级线程。因此,您不想创建大量工作线程,并且应该在 Web Worker 完成工作后终止它。工人可以终止自己,如下所示:

self.close();

或者父线程可以终止工作线程:

primeWorker.terminate();

安全和限制

在工作脚本中,我们无法访问许多重要的 JavaScript 对象,例如 documentwindowconsoleparent,最重要的是无法访问 DOM。没有 DOM 访问权限并且无法更新页面确实听起来限制太多,但这是一个重要的安全设计决策。想象一下,如果多个线程尝试更新同一元素,可能会造成严重破坏。因此,网络工作者生活在一个受限且线程安全的环境中。

话虽如此,您仍然可以使用worker来处理数据并将结果返回到主线程,然后主线程可以更新DOM。尽管他们被拒绝访问一些非常重要的 JavaScript 对象,但工作人员可以使用一些函数,例如 setTimeout()/clearTimeout()setInterval()/clearInterval()navigator 等。还可以在工作器内使用 XMLHttpRequestlocalStorage 对象。

同源限制

在工作者上下文中, selfthis 均指全局范围。

为了与服务器通信,工作人员必须遵循同源策略。例如,托管在 http://www.example.com/ 上的脚本无法访问 https://www.example.com/ 上的脚本。即使主机名相同,同源策略也规定协议也必须相同。通常,这不是问题。您很可能正在编写工作程序、客户端,并从同一域为它们提供服务,但了解限制总是有用的。

Google Chrome 的本地访问问题

Google Chrome 对本地访问工作程序设置了限制,因此您将无法在本地设置上运行这些示例。如果您想使用 Chrome,则必须在某个服务器上托管这些文件,或者在从命令行启动 Chrome 时使用 --allow-file-access-from-files 标志。对于 OS X,按如下方式启动 chrome:

$ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --allow-file-access-from-files

但是,不建议在生产环境中使用此标志。因此,最好的选择是将这些文件托管在网络服务器上,并在任何支持的浏览器中测试您的网络工作人员。

调试 Worker 和错误处理

无法访问 console 使得这有点不简单,但是借助 Chrome 开发工具,人们可以像调试任何其他 JavaScript 代码一样调试工作代码。

从 Web Worker 开始

要处理 Web Worker 抛出的任何错误,您可以侦听 error 事件,该事件会填充 ErrorEvent 对象。您可以检查该对象以了解错误的详细原因。

primeWorker.addEventListener('error', function(error){
    console.log(' Error Caused by worker: '+error.filename
        + ' at line number: '+error.lineno
        + ' Detailed Message: '+error.message);
});

多个工作线程

虽然多个工作线程在它们之间划分工作是很常见的,但需要注意的是。官方规范指定这些工作人员相对重量级,预计将是在后台运行的长期脚本。 Web Worker 不适合大量使用,因为它们的启动性能成本和每个实例的内存成本都很高。

共享工作人员简介

该规范概述了两种类型的工作人员:专用工作人员和共享工作人员。到目前为止,我们已经看到了敬业工人的例子。它们直接链接到其创建者脚本/页面,因为它们与创建它们的脚本/页面具有一对一的关系。另一方面,共享工作人员可以在同一个来源的所有页面之间共享(即:同一来源的所有页面或脚本都可以与共享工作人员通信)。

要创建共享工作线程,只需将脚本的 URL 或工作线程的名称传递给 SharedWorker 构造函数即可。

共享工作程序使用方式的主要区别在于,它们与 port 相关联,以跟踪访问它们的父脚本。

以下代码片段创建一个共享工作线程,注册回调以监听该工作线程发布的任何消息,并将消息发布到共享工作线程:

var sharedWorker = new SharedWorker('findPrime.js');
sharedWorker.port.onmessage = function(event){
    ...
}

sharedWorker.port.postMessage('data you want to send');

类似地,工作人员可以侦听 connect 事件,当新客户端尝试连接到工作人员时会收到该事件,然后相应地向其发送消息。

onconnect = function(event) {
    // event.source contains the reference to the client's port
    var clientPort = event.source;
    // listen for any messages send my this client
    clientPort.onmessage = function(event) {
        // event.data contains the message send by client
        var data = event.data;
        ....
        // Post Data after processing
        clientPort.postMessage('processed data');
    }
};

由于它们的共享性质,您可以在同一应用程序的不同选项卡中维护相同的状态,因为不同选项卡中的两个页面使用相同的共享工作脚本来维护和报告状态。有关共享工作人员的更多详细信息,我鼓励您阅读规范。


实际用例

Web Worker 不适合大量使用,因为它们的启动性能成本很高,每个实例的内存成本也很高。

现实生活中的场景可能是,您被迫处理同步第三方 API,该 API 强制主线程在继续执行下一条语句之前等待结果。在这种情况下,您可以将此任务委托给新生成的工作线程,以利用异步功能为您带来好处。

Web 工作人员还擅长轮询情况,在这种情况下,您可以在后台连续轮询目标,并在一些新数据到达时将消息发布到主线程。

您可能还需要处理服务器返回的大量数据。传统上,处理大量数据会对应用程序的响应能力产生负面影响,从而使用户体验变得不可接受。更优雅的解决方案是将处理工作分配给多个工作人员来处理数据的非重叠部分。

其他用例可能是在多个网络工作人员的帮助下分析视频或音频源,每个工作人员都处理问题的预定义部分。


结论

想象一下在单线程环境中与多个线程相关的强大功能。

与 HTML5 规范中的许多内容一样,Web Worker 规范也在不断发展。如果您打算成为网络工作者,那么看看规范不会有什么坏处。

对于使用当前版本的 Chrome、Safari 和 Firefox 的专业工作人员来说,跨浏览器支持相当不错。即使是 IE 也没有落后太多,IE10 占据了主导地位。但是,仅当前版本的 Chrome 和 Safari 支持共享工作线程。令人惊讶的是,Android 4.0 中提供的最新版本的 Android 浏览器不支持 Web Worker,尽管在 2.1 版本中支持了 Web Worker。 Apple 还从 iOS 5.0 开始提供了 Web Worker 支持。

想象一下在单线程环境中与多线程相关的强大功能。可能性是无限的!

The above is the detailed content of Start with a Web Worker. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn