<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Web Workers on Last Stand</title><link>https://amatsuzero.github.io/LastStand/tags/web-workers/</link><description>Recent content in Web Workers on Last Stand</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Wed, 06 Nov 2024 00:00:00 +0800</lastBuildDate><atom:link href="https://amatsuzero.github.io/LastStand/tags/web-workers/index.xml" rel="self" type="application/rss+xml"/><item><title>Web Workers</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/html/web-workers/</link><pubDate>Wed, 16 Oct 2024 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/html/web-workers/</guid><description>&lt;p&gt;在浏览器和服务器上，JavaScript程序在单个处理线程上运行。这意味着程序一次只能做一件事。简单来说，你的新电脑可能有32核的CPU，但当JavaScript应用程序运行时，其中31个核心都处于空闲状态。&lt;/p&gt;
&lt;p&gt;JavaScript的单线程避免了复杂的并发情况。如果两个线程同时尝试进行不兼容的更改会发生什么？例如，一个浏览器可能在另一个线程重定向到新的URL并从内存中清除该文档时正在更新DOM。Node.js、Deno和Bun都继承了浏览器的单线程引擎。&lt;/p&gt;
&lt;p&gt;这不是JavaScript特定的限制。大多数语言都是单线程的，但像PHP和Python这样的Web选项通常在Web服务器上运行，为每个用户请求启动解释器的新线程的不同实例。这会消耗资源，因此Node.js应用程序通常定义自己的Web服务器，该服务器在单个线程上运行，并异步处理每个传入的请求。&lt;/p&gt;
&lt;p&gt;Node.js的方法在处理高流量负载时可能更高效，但长时间运行的JavaScript函数会消耗效率收益。&lt;/p&gt;
&lt;p&gt;在我们演示如何使用Web Workers解决执行速度问题之前，我们首先要了解JavaScript的运行方式以及为什么长时间运行的函数会引发问题。&lt;/p&gt;
&lt;h2 id="javascript非阻塞io事件循环"&gt;JavaScript非阻塞I/O事件循环&lt;/h2&gt;
&lt;p&gt;你可能会认为一次只做一件事会导致性能瓶颈，但JavaScript是异步的，这就避免了大部分单线程处理问题，因为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不需要等待用户在网页上点击按钮。 浏览器触发一个事件，当点击发生时调用JavaScript函数。&lt;/li&gt;
&lt;li&gt;不需要等待Ajax请求的响应。 浏览器触发一个事件，在服务器返回数据时调用JavaScript函数。&lt;/li&gt;
&lt;li&gt;Node.js应用程序不需要等待数据库查询的结果。 运行时在数据可用时调用JavaScript函数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;JavaScript引擎运行一个事件循环。一旦代码的最后一条语句执行完毕，运行时会循环回来，检查是否有未完成的定时器、待处理的回调函数和数据连接，然后根据需要执行回调函数。 其他操作系统处理线程负责调用诸如HTTP请求、文件处理和数据库连接等输入/输出系统。它们不会阻塞事件循环。事件循环可以继续执行下一个在队列中等待的JavaScript函数。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/html/web-workers/image-01.webp"&gt;&lt;/p&gt;
&lt;p&gt;实质上，JavaScript引擎的主要责任是运行JavaScript代码。操作系统处理所有其他可能导致引擎调用JavaScript函数的I/O操作。&lt;/p&gt;
&lt;h2 id="长时间运行的javascript函数"&gt;长时间运行的JavaScript函数&lt;/h2&gt;
&lt;p&gt;JavaScript函数通常由事件触发。它们会进行一些处理，输出一些数据，并且大部分情况下会在毫秒级别内完成，以便事件循环可以继续运行。&lt;/p&gt;
&lt;p&gt;不幸的是，一些长时间运行的函数可能会阻塞事件循环。想象一下，你正在开发自己的图像处理函数（如锐化、模糊、灰度化等）。异步代码可以从文件中读取（或写入）数百万字节的像素数据，这对JavaScript引擎影响很小。然而，处理图像的JavaScript代码可能需要几秒钟来计算每个像素。该函数会阻塞事件循环，直到它完成，其他JavaScript代码无法运行。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在浏览器中，用户将无法与页面进行交互。他们无法点击、滚动或输入，并且可能会看到一个“脚本无响应”的错误，其中有停止处理的选项。&lt;/li&gt;
&lt;li&gt;对于Node.js服务器应用程序而言，情况更糟糕。在函数执行时，它无法响应其他请求。如果函数需要花费10秒钟来完成，那么在那个时间点访问的每个用户都必须等待最多10秒钟，即使他们没有处理图像。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;你可以通过将计算拆分为较小的子任务来解决这个问题。下面的代码使用传递的imageFn函数，最多处理1,000个像素（从一个数组中）。然后，它使用1毫秒的setTimeout延迟调用自身。事件循环的阻塞时间较短，这样JavaScript引擎可以在迭代之间处理其他传入的事件：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// pass callback function, image processing function, and input image
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// return an output image to a callback function
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;processImage&lt;/span&gt;( &lt;span style="color:#a6e22e"&gt;callback&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;imageFn&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; =&amp;gt; {}, &lt;span style="color:#a6e22e"&gt;imageIn&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [] ) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;chunkSize&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// pixels to process per iteration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// process first chunk
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;imageOut&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pointer&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;processChunk&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// process a chunk of data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;processChunk&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pointerEnd&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pointer&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;chunkSize&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// pass chunk to image processing function
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;imageOut&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;imageOut&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;concat&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;imageFn&lt;/span&gt;( &lt;span style="color:#a6e22e"&gt;imageIn&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;slice&lt;/span&gt;( &lt;span style="color:#a6e22e"&gt;pointer&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;pointerEnd&lt;/span&gt; ) )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;pointerEnd&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;imageIn&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;length&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// process next chunk after a short delay
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pointer&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pointerEnd&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;setTimeout&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;processChunk&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;callback&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// complete - return output image to callback function
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;callback&lt;/span&gt;( &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;imageOut&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这种方法可以防止脚本无响应，但不总是切实可行的。单个执行线程仍然会执行所有的工作，即使CPU可能有更多的处理能力。为了解决这个问题，我们可以使用Web Workers。&lt;/p&gt;</description></item></channel></rss>