网络工作者的终极指南

    克雷格的盾牌
    分享

    在本教程中,我们将介绍web worker,并演示如何使用它们来解决执行速度问题。

    内容:

    1. JavaScript非阻塞I/O事件循环
    2. 长时间运行的JavaScript函数
    3. Web Workers
    4. Browser Worker演示
    5. 服务器端Web Worker演示
    6. Node.js Workers的替代方案
    7. 结论

    浏览器和服务器上的JavaScript程序运行在一个处理线程上。这意味着程序一次只能做一件事。简单地说,您的新PC可能有一个32核CPU,但当您的JavaScript应用程序运行时,其中31个CPU处于空闲状态。

    JavaScript的单线程避免了复杂的并发情况。如果两个线程同时尝试进行不兼容的更改会发生什么?例如,浏览器可能正在更新DOM,而另一个线程重定向到一个新的URL并从内存中删除该文档。node . jsDeno,和Bun从浏览器继承了相同的单线程引擎。

    这不是特定于javascript的限制。大多数语言都是单线程的,但是web选项(如PHP和Python)通常运行在web服务器上,该服务器在每个用户请求的新线程上启动解释器的单独实例。这是资源密集型的,所以Node.js应用程序通常定义自己的web服务器,它运行在单个线程上,异步处理每个传入的请求。

    Node.js方法可以更有效地处理更高的流量负载,但长时间运行的JavaScript函数将抵消效率的提高。

    在我们演示如何解决web worker的执行速度问题之前,我们将首先研究JavaScript是如何运行的,以及为什么长时间运行的函数是有问题的。

    JavaScript非阻塞I/O事件循环

    你可能认为一次做一件事会导致性能瓶颈,但JavaScript是异步的,这避免了大多数单线程处理问题,因为:

    • 没有必要等待供用户单击网页上的按钮。

      浏览器引发一个事件调用JavaScript函数当点击发生时。

    • 没有必要等待对Ajax请求的响应。

      当服务器返回数据时,浏览器引发一个事件,调用JavaScript函数。

    • Node.js应用程序不需要等待用于数据库查询的结果。

      当数据可用时,运行时调用JavaScript函数。

    JavaScript引擎运行事件循环.一旦代码的最后一条语句完成执行,运行时将返回并在必要时执行回调之前检查未执行的计时器、挂起的回调和数据连接。

    其他操作系统处理线程负责调用输入/输出系统,如HTTP请求、文件处理程序和数据库连接。它们不会阻塞事件循环.它可以继续执行等待队列中的下一个JavaScript函数。

    简化的JavaScript事件循环

    本质上,JavaScript引擎只负责运行JavaScript代码。操作系统处理所有其他I/O操作,这些操作可能导致引擎在发生某些事情时调用JavaScript函数。

    长时间运行的JavaScript函数

    JavaScript函数通常由事件触发。它们将进行一些处理,输出一些数据,大多数情况下,将在几毫秒内完成,以便事件循环可以继续。

    不幸的是,一些长时间运行的函数会阻塞事件循环。假设您正在开发自己的图像处理功能(如锐化、模糊化、灰度化等)。异步代码可以从(或向)文件读取(或写入)数百万字节的像素数据——这对JavaScript引擎几乎没有影响。然而,处理图像的JavaScript代码可能需要几秒钟来计算每个像素。函数阻塞事件循环—并且在它完成之前,没有其他JavaScript代码可以运行

    • 在浏览器中,用户将无法与页面交互。他们将无法单击、滚动或键入,并且可能会看到“无响应脚本”错误,并选择停止处理。

    • Node.js服务器应用程序的情况更糟。它不能在函数执行时响应其他请求。如果它需要十秒钟才能完成,每一个此时用户访问将不得不等待长达10秒即使他们没有处理图像

    您可以通过将计算拆分为更小的子任务来解决这个问题。下面的代码使用传入的参数处理不超过1,000个像素(来自数组)<代码>我mageFn函数。然后它用a调用自己<代码>setTimeout延迟1毫秒。事件循环阻塞的时间较短,这样JavaScript引擎就可以在迭代之间处理其他传入事件:

    //传递回调函数,图像处理函数,输入图像返回一个输出图像到回调函数函数processImage回调imageFn= >{的日记]{常量chunkSize1000//每次迭代要处理的像素//处理第一个块imageOut]指针0processChunk//处理数据块函数processChunk{常量pointerEnd指针+chunkSize//传递块到图像处理函数imageOutimageOutconcatimageFn的日记指针pointerEnd如果pointerEnd<的日记长度{//在短暂延迟后处理下一个块指针pointerEndsetTimeoutprocessChunk1其他的如果回调{// complete -将输出图像返回回调函数回调imageOut

    这可以防止无响应的脚本,但并不总是实用的。单个执行线程仍然完成所有工作,即使CPU可能有能力做更多的工作。为了解决这个问题,我们可以使用网络工作者。

    Web Workers

    Web workers允许脚本作为后台线程运行。worker使用独立于主执行线程的引擎实例和事件循环运行。它在不阻塞主事件循环和其他任务的情况下并行执行。

    使用工作脚本:

    1. 主线程发布一条包含所有必要数据的消息。
    2. worker中的事件处理程序执行并启动计算。
    3. 完成时,worker将带有返回数据的消息发送回主线程。
    4. 主线程中的事件处理程序执行、解析传入数据并采取必要的操作。

    工人处理

    主线——或任何工人-可以产生任意数量的工人。多个线程可以并行处理单独的数据块,从而比单个后台线程更快地确定结果。也就是说,每个新线程都有启动开销,因此确定最佳平衡可能需要一些实验。

    所有的浏览器,Node.js 10+, Deno和Bun都支持类似的语法,尽管服务器运行时可以提供更高级的选项。

    Browser Worker演示

    下面的演示展示了一个毫秒级的数字时钟,每秒更新60次。同时,你可以启动一个骰子模拟器,投掷任意次数的骰子。默认情况下,它会投掷10个六面骰子1000万次,并记录总数的频率。

    在CodeSandbox上查看以上演示:

    点击开始扔看时钟;它将在计算运行时暂停。较慢的设备和浏览器可能会抛出“无响应脚本”错误。

    现在检查使用web worker复选框,然后再次开始投掷。在计算过程中,时钟继续运行。这个过程可能需要稍长的时间,因为web worker必须启动、接收数据、运行计算并返回结果。随着计算复杂性或迭代的增加,这一点将不那么明显。在某些时候,工作线程应该比主线程快。

    专职工作者vs共享工作者

    浏览器提供两个worker选项:

    • 专门的工人:由另一个脚本启动、使用和终止的单个脚本

    • 共享的工人:一个单一的脚本,可以在不同的窗口、iframe或worker中被多个脚本访问

    与共享工作者通信的每个脚本都传递一个惟一的端口,共享工作者必须使用该端口将数据传递回去。然而,IE或大多数移动浏览器不支持共享工作,这使得它们在典型的web项目中无法使用。

    客户端工作人员限制

    一个worker独立于主线程和其他worker运行;它不能访问其他线程中的数据,除非该数据显式地传递给worker。一个复制的数据被传递给worker。在内部,JavaScript使用它的结构化克隆算法将数据序列化为字符串。它可以包括本机类型,如字符串、数字、布尔值、数组和对象,但是函数或DOM节点。

    浏览器工作者可以使用控制台、Fetch、XMLHttpRequest、WebSocket和IndexDB等api。它们不能访问文档对象、DOM节点、localStorage和窗口对象的某些部分,因为这可能导致JavaScript用单线程解决的并发冲突问题——比如在重定向的同时更改DOM。

    重要提示:worker最适合用于cpu密集型任务。它们对密集的I/O工作没有好处,因为这些工作被卸载给浏览器,并且是异步运行的。

    如何使用客户端网络工作者

    下面的演示定义了<代码>src/ index.js作为主脚本,在用户单击时启动时钟并启动web worker开始按钮。它定义了工人对象工作脚本的名称为<代码>src/ worker.js(相对于HTML文件):

    //在工作线程上运行常量工人工人”。/ src / worker.js”

    一个<代码>onmessage下面是事件处理程序。它在worker将数据发送回主脚本时运行——通常在计算结束时运行。数据可在事件对象的<代码>数据属性,并将其传递给<代码>endDiceRun ()功能:

    //从worker接收数据工人onmessage函数e{endDiceRune数据

    主脚本使用its启动工作<代码>postMessage()方法发送数据(一个名为<代码>cfg):

    //向worker发送数据工人postMessagecfg

    的<代码>src/ worker.js定义工作代码。进口<代码>src/ dice.js使用importScripts ()-一个全局工作方法,同步导入一个或多个脚本到工作中。文件引用相对于工作者的位置:

    importScripts”。/ dice.js '

    src / dice.js定义了一个<代码>d我ceRun ()函数计算投掷统计信息:

    //记录投掷骰子的统计信息函数diceRun运行1骰子26{常量统计]运行>0{总和0d骰子d>0d--{总和+ =数学地板上数学随机+1统计总和]统计总和]||0+1运行--返回统计

    注意这是ES模块(见下文).

    src / worker.js然后定义一个<代码>onmessage()事件处理程序。当主调用脚本(<代码>src/ index.js)向worker发送数据。事件对象具有<代码>.数据属性,该属性提供对消息数据的访问。在这种情况下,它是<代码>cfg对象的属性<代码>.throws,<代码>.dice,<代码>.sides,它们作为参数传递给<代码>d我ceRun ():

    onmessage函数e{//开始计算常量cfge数据常量统计diceRuncfg抛出cfg骰子cfg//返回主线程postMessage统计

    一个<代码>postMessage()函数将结果发送回主脚本。这调用<代码>worker.onmessage上面所示的处理程序,它运行<代码>endDiceRun ().

    总之,线程处理是通过在主脚本和worker之间发送消息来实现的:

    1. 主脚本定义了一个<代码>工人对象和调用<代码>postMessage()发送数据。
    2. 工作脚本执行一个<代码>onmessage开始计算的处理程序。
    3. 工人喊道<代码>postMessage()将数据发送回主脚本。
    4. 主脚本执行一个<代码>onmessage处理程序来接收结果。

    工作者消息调用

    Web工作人员错误处理

    除非你使用的是旧的应用程序,现代浏览器中的开发人员工具像任何标准脚本一样支持web worker调试和控制台日志记录。

    主脚本可以调用.terminate ()方法在任何时候结束该worker。如果工作人员未能在特定时间内作出回应,这可能是必要的。例如,如果一个活跃的worker在10秒内没有收到响应,这段代码将终止它:

    //主脚本常量工人工人“/ src / worker.js。”//在10秒后终止worker常量workerTimersetTimeout= >工人终止10000// worker complete工人onmessage函数e{//取消终止clearTimeoutworkerTimer// start worker工人postMessage{somedata1

    Worker脚本可以使用标准的错误处理技术,比如验证传入数据,试一试,<代码>抓,<代码>最后,优雅地处理出现的问题,并在需要时向主脚本报告。

    你可以在主脚本中使用以下方法检测未处理的worker错误:

    • onmessageerror:当worker接收到不能反序列化的数据时触发

    • onerror:当工作脚本中发生JavaScript错误时触发

    返回的事件对象中提供错误详细信息<代码>.filename,<代码>.lineno,<代码>.message属性:

    //检测工作人员错误工人onerror函数犯错{控制台日志$ {犯错文件名、线$ {犯错lineno$ {犯错消息

    客户端web worker和ES模块

    默认情况下,浏览器web worker是能够使用ES模块(使用<代码>出口而且<代码>进口语法)。

    的<代码>src/ dice.jsFile定义了一个导入到worker中的函数:

    importScripts”。/ dice.js '

    有点不同寻常的是,<代码>src/ dice.js代码也包含在main中<代码>src/ index.js脚本,因此它可以启动与工作进程和非工作进程相同的函数。<代码>src/ index.js加载为ES模块。它不能<代码>进口的<代码>src/ dice.js代码,但它可以加载它作为HTML<代码><>脚本元素,使其在模块中可用:

    常量diceScript文档createElement“脚本”diceScriptsrc“/ src / dice.js。”文档列表末尾diceScript

    这种情况不太可能发生在大多数应用程序中,除非需要在主脚本和辅助脚本之间共享代码库。

    可以通过在workers中添加<代码>{类型:"module"}工作构造函数的参数:

    常量工人工人“/ src / worker.js。”{类型“模块”

    然后你就可以<代码>出口的<代码>d我ceRun ()函数<代码>src/ dice.js:

    出口函数diceRun运行1骰子26{/ /……

    然后<代码>进口它在<代码>worker.js模块使用完全限定或相对URL引用:

    进口{diceRun”。/ dice.js '

    从理论上讲,ES6模块是一个很好的选择,但不幸的是,它们只在基于chromium的浏览器的80版本(2020年发布)中得到支持。您不能在Firefox或Safari中使用它们,这使得它们对于本文所示的示例代码不切实际。

    更好的选择是使用捆扎器,例如esbuildrollup.js.它们可以解析ES模块引用,并将它们打包到单个工作(和主)JavaScript文件中。这简化了编码,并大大提高了worker的速度,因为它们不需要在执行之前解析导入。

    客户端服务工作者

    服务工作人员是Progressive web Apps用来提供离线功能、后台数据同步和web通知的特殊网络工作者。他们可以:

    • 充当浏览器和网络之间的代理,以管理缓存文件
    • 在后台运行,即使浏览器或页面没有加载更新数据和接收传入消息

    与web worker一样,service worker在单独的处理线程上运行,不能使用DOM之类的api。然而,相似之处也仅此而已:

    • 主线程可以声明service worker的可用性,但两者之间没有任何直接通信。主线程不一定知道service worker正在运行。

    • Service worker通常不用于cpu密集型计算。它们可以通过缓存文件和进行其他网络优化来间接提高性能。

    • 一个特定的域/路径可以为不同的任务使用许多web worker,但它只能注册一个service worker。

    • Service worker必须在相同的HTTPS域和路径上,而web worker可以在HTTP上的任何域或路径上操作。

    服务工作者超出了本文的范围,但您可以找到更多信息:

    服务器端Web Worker演示

    Node.js是最常用的服务器JavaScript运行时,它从版本10开始提供worker。

    Node.js并不是唯一的服务器运行时:

    • Deno复制了Web Worker API,因此语法与浏览器代码相同。它还提供了一个兼容模式,如果你想使用运行时的,它可以填充Node.js api工作线程语法

    • 包子虽然它的目的是同时支持浏览器和Node.js工作api。

    • 您可能正在使用JavaScript无服务器服务,如AWS Lambda、Azure函数、谷歌云函数、Cloudflare workers或Netlify边缘函数等。这些可能会提供类似web worker的api,尽管好处较少,因为每个用户请求都会启动一个单独的隔离实例。

    下面的演示展示了一个Node.js进程,它每秒钟将当前时间写入控制台:在新的浏览器选项卡中打开Node.js演示

    然后在主线程上启动掷骰子计算。暂停当前时间输出:

    计时器进程12:33:18 PM计时器进程12:33:19 PM计时器进程12:33:20 PM没有线程计算开始…┌─────────┬──────────┐│││值(指数)├─────────┼──────────┤2 2776134│││││5556674 3│││8335819 4│││11110893 5│││13887045 6│││16669114 7│││13885068 8│││11112704 9│││10 8332503│││5556106 11│││2777940 12│└─────────┴──────────┘处理时间:2961 ms没有线程计算完成定时器进程12:33:24点

    一旦完成,同样的计算在工作线程上启动。在这种情况下,当骰子处理发生时,时钟继续运行:

    工人计算开始…定时器进程12:33:27点定时器进程12:33:28点定时器进程12:33:29点┌─────────┬──────────┐││(指数)值│├─────────┼──────────┤2 2778246│││││3 5556129││4 8335780││││11114930 5│││6 13889458│││7 16659456│││13889139 8│││11111219 9│││10 8331738│││11 5556788│││12 2777117│└─────────┴──────────┘处理时间:2643 ms工人计算完成定时器过程12:33:30点定时器进程12:33:31点定时器进程12:33:32点

    工作进程通常比主线程快一点。

    如何使用服务器端网络工作者

    演示定义了<代码>src/ index.js作为主脚本,其中开始一个<代码>计时器进程(如果它还没有运行),当服务器收到一个新的HTTP请求:

    / /定时器计时器setInterval= >{控制台日志计时器的过程$ {intlTime格式日期1000

    的<代码>runWorker ()函数定义了工人对象工作脚本的名称为<代码>src/ worker.js(相对于项目根目录)。它通过一个<代码>workerData变量作为单个值,在这种情况下,是一个具有三个属性的对象:

    常量工人工人”。/ src / worker.js”{workerData{抛出骰子

    与浏览器web worker不同,这将启动脚本。没必要跑<代码>工人.postMessage(),尽管您可以使用它来运行<代码>parentPort.在(“信息”)在工作者中定义的事件处理程序。

    的<代码>src/ worker.js代码调用<代码>d我ceRun ()与<代码>workerData值并将结果传递回主线程<代码>parentPort.postMessage():

    //工作线程进口{workerDataparentPort“节点:worker_threads”进口{diceRun”。/ dice.js”//开始计算常量统计diceRunworkerData抛出workerData骰子workerData//发送消息到父脚本parentPortpostMessage统计

    这就引发了<代码>“消息”主事件<代码>src/ index.js脚本,该脚本接收结果:

    //返回结果工人“消息”结果= >{控制台表格结果

    工作线程在发送消息后终止,这将引发<代码>“退出”事件:

    工人“退出”代码= >{/ /……清理

    你可以根据需要定义其他错误和事件处理程序:

    • messageerror:当worker接收到不能反序列化的数据时触发
    • 在线:当工作线程开始执行时触发
    • 错误:当工作脚本中出现JavaScript错误时触发。

    内联工作脚本

    一个脚本文件可以包含这两个主代码和辅助代码。代码可以检查它是否运行在使用的主线程上<代码>我sMainThread,然后称自己为worker(使用<代码>我mport.meta.url作为ES模块中的文件引用,或者<代码>__filename在CommonJS):

    进口{工人isMainThreadworkerDataparentPort“节点:worker_threads”如果isMainThread{//主线程//创建一个worker常量工人工人进口url{workerData{抛出骰子工人“消息”味精= >{工人“退出”代码= >{其他的{//工作线程常量统计diceRunworkerData抛出workerData骰子workerDataparentPortpostMessage统计

    就我个人而言,我更喜欢分开文件,因为主线程和工作线程可能需要不同的模块。对于简单的单脚本项目,内联工作者可以是一种选择。

    服务器端工作人员限制

    服务器工作者仍然独立运行,并像在浏览器中一样接收有限的数据副本。

    Node.js、Deno和Bun中的服务器端工作线程比浏览器工作线程有更少的API限制,因为没有DOM。当两个或多个worker试图同时向同一个文件写入数据时,可能会出现问题,但在大多数应用程序中不太可能发生这种情况。

    您将无法传递和共享复杂的对象,例如数据库连接,因为大多数对象都有无法克隆的方法和函数。然而,你可以做以下其中之一:

    • 异步读取主线程中的数据库数据,并将结果数据传递给worker。

    • 在worker中创建另一个连接对象。这将有启动成本,但如果函数需要进一步的数据库查询作为计算的一部分,这可能是可行的。

    重要提示:请记住,worker最适合用于cpu密集型任务。它们对密集的I/O工作没有好处,因为那是卸载给操作系统的,并且是异步运行的。

    线程间共享数据

    上面所示的主线程和工作线程之间的通信导致了双方的克隆数据。可以在线程之间共享数据SharedArrayBuffer对象,表示固定长度的原始二进制数据。下面的主线程定义了100个从0到99的数字元素,并将它们发送给一个worker:

    进口{工人“节点:worker_threads”常量缓冲SharedArrayBufferOne hundred.Int32ArrayBYTES_PER_ELEMENT价值Int32Array缓冲价值forEachv= >价值]常量工人工人”。/ worker.js”工人postMessage{价值

    工人可以收到<代码>价值对象:

    进口{parentPort“节点:worker_threads”parentPort“消息”价值= >{价值0]One hundred.

    的元素中,主线程或工作线程都可以更改元素<代码>价值数组,两边都改变了。

    这种技术可以提高效率,因为不需要在两个线程中序列化数据。也有缺点:

    • 只能共享整数。
    • 仍然有必要发送一条消息来表明数据已经更改。
    • 存在两个线程同时更改相同值并失去同步的风险。

    也就是说,这一过程有利于那些需要处理大量图像或其他数据的高性能游戏。

    Node.js Workers的替代方案

    并不是每个Node.js应用程序都需要或可以使用worker。一个简单的web服务器应用程序可能没有复杂的计算。它继续在单个处理线程上运行,并且随着活动用户数量的增加,它的响应将变得不那么灵敏。设备可能具有相当大的处理能力,多个CPU核心仍未使用。

    下面几节描述通用多线程选项。

    Node.js子进程

    Node.js在worker和Deno和Bun之前支持子进程。

    本质上,它们可以启动另一个应用程序(不一定用JavaScript)、传递数据并接收结果。他们的工作方式与工人类似,但通常效率较低,流程更密集。

    当您运行复杂的JavaScript函数(可能在同一个项目中)时,最好使用worker。当您启动另一个应用程序(如Linux或Python命令)时,子进程是必要的。

    node . js集群

    node . js集群允许您派生任意数量的相同进程,以更有效地处理负载。初始的主进程可以fork自己——可能对于返回的每个CPU一次os.cpus ().它还可以处理实例失败时的重启,并代理fork进程之间的通信消息。

    的<代码>集群标准库提供的属性和方法包括:

    • .isPrimary:返回<代码>真正的对于主要的主进程(较旧的<代码>.isMaster也支持)

    • .fork ():生成child worker进程

    • .isWorker:返回<代码>真正的对于工作进程

    这个例子为设备上可用的每个CPU/核心启动一个web服务器工作进程。一台4核机器将生成4个web服务器实例,因此它可以处理多达4倍的处理负载。它还会重新启动任何失败的进程,以使应用程序更加健壮:

    / / app.js进口集群节点:集群的进口过程节点:过程的进口{cpu节点:操作系统的进口http节点:http的常量cpucpu长度如果集群isPrimary{控制台日志启动主进程:$ {过程pid// fork worker0<cpu++{集群// worker失败事件集群“退出”工人代码信号= >{控制台日志工人$ {工人过程pid失败的集群其他的{//启动HTTP服务器httpcreateServer要求的事情res= >{reswriteHead200res结束“你好!”8080控制台日志已启动工作进程:$ {过程pid

    所有进程共享端口8080,任何进程都可以处理传入的HTTP请求。运行应用程序时的日志显示如下内容:

    启动主进程:1001已启动工作进程:1002已启动工作进程:1003已启动工作进程:1004已启动工作进程:1005.....工人1002failed启动worker进程:1006

    很少有Node.js开发人员尝试集群。上面的示例很简单,而且效果很好,但是当您试图处理消息、失败和重新启动时,代码可能会变得越来越复杂。

    流程管理人员

    Node.js进程管理器可以帮助运行一个Node.js应用程序的多个实例,而无需手动编写集群代码。最著名的是PM2.下面的命令为每个CPU/核启动应用程序的实例,并在它们失败时重新启动任何实例:

    Pm2 start ./app.js -i Max .js

    应用程序实例在后台启动,所以它非常适合在活动服务器上使用。您可以通过输入来检查哪些进程正在运行<代码>pm2状态(节录输出):

    pm2美元地位┌────┬──────┬───────────┬─────────┬─────────┬──────┬────────┐│id│name│命名空间│版本│模式│pid│正常运行时间│├────┼──────┼───────────┼─────────┼─────────┼──────┼────────┤│1│app│default1.0.0│集群│1001│4d││2│app│default1.0.0│集群│10024 d││└────┴──────┴───────────┴─────────┴─────────┴──────┴────────┘

    PM2还可以运行用Deno、Bun、Python或任何其他语言编写的非node .js应用程序。

    容器管理

    集群和进程管理器将应用程序绑定到特定的设备。如果您的服务器或操作系统依赖项失败,无论运行的实例数量有多少,应用程序都将失败。

    容器的概念与虚拟机类似,不同之处在于,容器模拟的不是完整的硬件设备,而是操作系统。容器是围绕单个应用程序的轻量级包装,其中包含所有必要的操作系统、库和可执行文件。它提供了Node.js(或任何其他运行时)和应用程序的隔离实例。单个设备可以运行多个容器,因此不需要集群或进程管理。

    容器超出了本文的范围,但是众所周知的解决方案包括码头工人而且Kubernetes.他们可以在分布传入流量的同时,在任意数量的设备上启动和监视任意数量的容器,甚至在不同的位置。

    结论

    JavaScript worker可以通过在并行线程中运行cpu密集型计算来提高客户机和服务器上的应用程序性能。服务器端工作者还可以在单独的线程中运行更危险的函数,并在处理时间超过一定限制时终止它们,从而使应用程序更加健壮。

    在JavaScript中使用worker很简单,但是:

    • 工作人员不能访问所有api,比如浏览器DOM。它们最适合用于长时间运行的计算任务。

    • 对于密集但异步的I/O任务(如HTTP请求和数据库查询),worker不太需要。

    • 启动一个worker是有开销的,所以可能需要一些实验来确保它们提高性能。

    • 进程和容器管理等选项可能比服务器端多线程更好。

    也就是说,当你遇到绩效问题时,员工是一个有用的考虑工具。

    Baidu