首页 > web前端 > js教程 > JavaScript中的多线程

JavaScript中的多线程

Christopher Nolan
发布: 2025-02-25 11:37:12
原创
885 人浏览过

JavaScript中的多线程

好吧,在我们开始之前,让我清理并承认本文的标题有点耸人听闻! JavaScript实际上没有多线程功能,JavaScript程序员无能为力。在所有浏览器中 - 除了Google Chrome - JavaScript都以单个执行线程运行,这就是它的方式。

> 但是,我们能做的是模拟

多线程,这在多线程环境中带来了一个好处之一:它允许我们运行极其密集的代码。这是代码,否则将冻结浏览器并在Firefox中生成其中一个“反应式脚本”警告。> 钥匙要点

JavaScript

>不本质地支持多线程,而是通过异步计时器和网络工作人员进行模拟,允许密集的计算而无需冷冻浏览器。
    >异步计时器将任务分解为较小的块,从而通过管理代码如何随着时间的推移来执行方式来防止浏览器变得无响应。
  • >
  • > Web工作者增强了JavaScript执行背景任务而不会影响用户界面的能力,尽管他们无法与DOM进行交互或使用某些Web API。
  • 对于冒着锁定浏览器的复杂操作,使用异步计时器进行重构代码可以防止UI冻结并更有效地管理资源密集型过程。
  • 尽管JavaScript具有单线程的性质,但诸如异步编程和网络工作人员之类的技术为开发人员提供了强大的工具来处理多线程式的方案,从而提高了强化任务的性能。
  • 时间等待没有一个人
  • >这一切都取决于使用异步计时器的使用。当我们在异步计时器中运行重复代码时,我们将为浏览器的脚本解释器处理每次迭代的时间。
  • > 有效地,迭代器中的一块代码正在要求解释器立即完成所有操作:“运行此代码 n
  • 尽可能快地运行。”但是,异步计时器内的相同代码将代码分解成小谨慎的块。也就是说,“运行此代码一次尽可能快”,然后等待 - 然后“尽可能快地运行此代码”,依此类推,
诀窍是,每次迭代内部的代码都很小且简单,以使解释器在计时器速度(无论是100或5,000毫秒)的速度以内处理。如果满足了该要求,那么整体代码的强度都没有关系,因为我们并不要求一次运行它。

>

“太激烈”多么激烈?
> 通常,如果我编写一个被证明太密集的脚本,我会考虑重新设计它;这样的显着放缓通常表明代码存在问题,或者是应用程序设计的更深层次问题。

,但有时不是。有时根本无法避免特定操作的强度,而根本没有在JavaScript中进行。 在给定情况下,这可能是最好的解决方案;也许在应用程序中的某些处理需要移至服务器端,在该服务器端,它具有更多的处理能力,通常可以使用,以及真正的线程执行环境(Web服务器)。

>但最终您可能会发现这不是一个选择的情况 - JavaScript简单地

必须

能够做某事或该死。这就是我在开发Firefox扩展时发现自己的情况。 该扩展名的核心是测试适用于页面的CSS选择器的能力,以查看是否实际使用它们。此的本质是使用Dean Edwards'Base2:的Matchall()方法的一组评估

>肯定足够直接。但是agetall()本身非常强烈,就像它一样 - 要解析和评估任何CSS1或CSS2选择器,然后走整个DOM树寻找匹配;并且扩展程序是为的每个单独的选择器的,其中可能有几千个。表面上如此简单,该过程可能是如此密集,以至于整个浏览器发生在发生时。这就是我们发现的。

锁定浏览器显然不是一个选项,因此,如果这根本无法使用,我们必须找到一种使它运行而不会出错的方法。

>

一个简单的测试用例
for(var i=0; i<selectors.length; i++) <br>
{ <br>
  if(base2.DOM.Document.matchAll <br>
    (contentdoc, selectors[i]).length > 0) <br>
  { <br>
    used ++; <br>
  } <br>
  else <br>
  { <br>
    unused ++; <br>
  } <br>
}
登录后复制
登录后复制
登录后复制
登录后复制

>让我们通过一个简单的测试案例来证明问题,涉及两个迭代级别;内部层面故意太密集,因此我们可以创建比赛条件,而外部层面相当短,因此它可以模拟主代码。这就是我们所拥有的:

>我们从简单的表单(这是测试代码,而不是生产)开始测试,并获取输出

现在,让我们在Firefox中运行该代码(在这种情况下,在2GHz MacBook上使用Firefox 3)……并且正如预期的那样,浏览器UI在运行时冻结(例如,不可能,例如不可能按刷新并放弃该过程) 。大约90次迭代后,Firefox产生了一个“反应式脚本”警告对话框。

function process() <br>
{ <br>
  var above = 0, below = 0; <br>
  for(var i=0; i<200000; i++) <br>
  { <br>
    if(Math.random() * 2 > 1) <br>
    { <br>
      above ++;       <br>
    } <br>
    else <br>
    { <br>
      below ++; <br>
    } <br>
  } <br>
} <br>
 <br>
 <br>
function test1() <br>
{ <br>
  var result1 = document.getElementById('result1'); <br>
   <br>
  var start = new Date().getTime(); <br>
     <br>
  for(var i=0; i<200; i++) <br>
  { <br>
    result1.value =  'time=' +  <br>
      (new Date().getTime() - start) + ' [i=' + i + ']'; <br>
     <br>
    process(); <br>
  } <br>
   <br>
  result1.value = 'time=' +  <br>
    (new Date().getTime() - start) + ' [done]'; <br>
}
登录后复制
登录后复制
登录后复制
如果我们允许它继续下去,在另外90个迭代后,Firefox再次产生相同的对话框。在这方面,Safari 3和Internet Explorer 6的行为类似,并具有冻结的UI和一个警告对话框的阈值。在Opera中没有这样的对话框 - 它只是继续运行代码直到完成代码 - 但是浏览器UI类似地冻结了直到完成任务。

显然,在实践中我们无法运行代码。因此,让我们重新开始并使用异步计时器进行外循环:>

>现在让我们再次运行……这一次我们会收到完全不同的结果。该代码需要一段时间才能完成,但是它可以一直成功地运行到末端,而没有UI冻结,并且没有警告过度缓慢的脚本。

for(var i=0; i<selectors.length; i++) <br>
{ <br>
  if(base2.DOM.Document.matchAll <br>
    (contentdoc, selectors[i]).length > 0) <br>
  { <br>
    used ++; <br>
  } <br>
  else <br>
  { <br>
    unused ++; <br>
  } <br>
}
登录后复制
登录后复制
登录后复制
登录后复制
>查看测试页面

(繁忙的标志用于防止计时器实例发生碰撞。如果我们已经在下一次迭代时已经处于子过程中的中间,我们只需等待以下迭代,从而确保只有一个子过程一次运行。)

>因此,您可以看到,尽管我们可以在

> Inner

过程中所做的工作仍然很小,但

的次数>我们可以运行该过程是无限的:我们可以运行该过程外循环基本上永远是永远的,浏览器将永远不会冻结。 >更喜欢它 - 我们可以在野外使用它。 你疯了!

>我已经可以听到反对者了。实际上,我可能自己是一个人:您为什么要这样做 - 什么样的疯子坚持将JavaScript推到从未被设计为去的所有这些地方?您的代码太强烈了。这是工作的错误工具。如果您必须跳过这类箍,那么您的应用程序的设计从根本上是错误的。

>我已经提到了一个例子,我必须找到一种繁重的脚本工作的方法。就是这样,或者整个想法必须放弃。如果您不相信该答案,那么本文的其余部分也可能对您不吸引您。

>但是,如果您是 - 或者至少,如果您愿意说服它,这是另一个真正可以将其钉在家里的示例:使用JavaScript编写可以在计算机上进行比赛的游戏。>>>>>

>上的游戏

我在这里说的是了解游戏规则所需的代码,然后可以评估情况和策略,以便在该游戏中击败您。复杂的东西。

为了说明,我要看一个我在一边开发的项目一段时间。 “小时”是指三年

,其中大多数是在理论上工作的高原上花费的,但太强烈了,无法使用……直到我想到了这种方法。该游戏是一个基于颜色和形状匹配的竞争难题。

>

JavaScript中的多线程

总结一下:您通过相邻的形状和颜色匹配来整个过程。例如,如果您开始使用绿色三角形 - 那么您可以移至任何其他三角形或任何其他绿色形状。您的目标是到达中间的水晶,然后将其带到板的另一侧,而对手则尝试这样做。您也可以从对手那里窃取水晶。

因此,我们有确定运动的逻辑规则,我们还可以看到策略的出现。例如,为了避免让对手到达水晶,或从您那里偷走它 - 您可能会选择阻止它们的动作,或者尝试在他们无法到达的地方完成。

计算机的工作是在任何给定的情况下找到最佳的动作,因此让我们查看摘要伪代码中的该过程:

>

for(var i=0; i<selectors.length; i++) <br>
{ <br>
  if(base2.DOM.Document.matchAll <br>
    (contentdoc, selectors[i]).length > 0) <br>
  { <br>
    used ++; <br>
  } <br>
  else <br>
  { <br>
    unused ++; <br>
  } <br>
}
登录后复制
登录后复制
登录后复制
登录后复制
我们评估了一种策略,如果这给了我们一个好的举动,那么我们就完成了;否则,我们将评估另一种策略,依此类推,直到我们有动作,或者得出结论,我们必须通过。

>每个策略功能都运行一个昂贵的过程,因为它必须评估董事会中的每个职位以及潜在的未来位置,可能会根据各种因素进行多次。该示例只有三种策略,但是在真实的游戏中,有数十种可能性,每个可能性都昂贵。

>

>单独评估中的任何一个都很好,但是所有这些评估都可以连续运行,使过度激烈的过程冻结了浏览器。

>

>因此,我所做的是将主代码拆分为“谨慎”任务

,每个任务都是用开关语句选择的,然后使用异步计时器进行迭代。这样做的逻辑距离那些小时候的那些选择您曾经有过的冒险书的逻辑不到一百万英里,在那里,每个任务都以实时的选择结束,直到我们到达终点:>

此代码比原始代码明显高得多,因此,如果减少代码大小是唯一的命令,这显然不是要走的路。

但是,我们在这里要做的是创建一个没有天花板的执行环境,即,在复杂性和长度方面没有上限的过程;这就是我们所做的。
function process() <br>
{ <br>
  var above = 0, below = 0; <br>
  for(var i=0; i<200000; i++) <br>
  { <br>
    if(Math.random() * 2 > 1) <br>
    { <br>
      above ++;       <br>
    } <br>
    else <br>
    { <br>
      below ++; <br>
    } <br>
  } <br>
} <br>
 <br>
 <br>
function test1() <br>
{ <br>
  var result1 = document.getElementById('result1'); <br>
   <br>
  var start = new Date().getTime(); <br>
     <br>
  for(var i=0; i<200; i++) <br>
  { <br>
    result1.value =  'time=' +  <br>
      (new Date().getTime() - start) + ' [i=' + i + ']'; <br>
     <br>
    process(); <br>
  } <br>
   <br>
  result1.value = 'time=' +  <br>
    (new Date().getTime() - start) + ' [done]'; <br>
}
登录后复制
登录后复制
登录后复制

这个模式可以无限期地扩展

,具有数百甚至数千个任务。它可能需要很长时间才能运行,但是将其运行,并且只要每个

个体

任务都不太强烈,它将在不杀死浏览器的情况下运行。>

无返回的路径 这种方法的强度也是它的主要弱点:由于内部函数是异步的,因此我们无法从外部函数中返回值。因此,例如,我们无法做到这一点(或者,我们可以,但没有意义):>

for(var i=0; i<selectors.length; i++) <br>
{ <br>
  if(base2.DOM.Document.matchAll <br>
    (contentdoc, selectors[i]).length > 0) <br>
  { <br>
    used ++; <br>
  } <br>
  else <br>
  { <br>
    unused ++; <br>
  } <br>
}
登录后复制
登录后复制
登录后复制
登录后复制

checksomething()函数将始终始终返回false,因为内部函数是异步的。外部功能将在内部功能的第一次迭代发生之前返回!

下一个示例同样毫无意义:

function process() <br>
{ <br>
  var above = 0, below = 0; <br>
  for(var i=0; i<200000; i++) <br>
  { <br>
    if(Math.random() * 2 > 1) <br>
    { <br>
      above ++;       <br>
    } <br>
    else <br>
    { <br>
      below ++; <br>
    } <br>
  } <br>
} <br>
 <br>
 <br>
function test1() <br>
{ <br>
  var result1 = document.getElementById('result1'); <br>
   <br>
  var start = new Date().getTime(); <br>
     <br>
  for(var i=0; i<200; i++) <br>
  { <br>
    result1.value =  'time=' +  <br>
      (new Date().getTime() - start) + ' [i=' + i + ']'; <br>
     <br>
    process(); <br>
  } <br>
   <br>
  result1.value = 'time=' +  <br>
    (new Date().getTime() - start) + ' [done]'; <br>
}
登录后复制
登录后复制
登录后复制

>我们不超出外部功能的范围,因此我们无法从中返回;该返回值无用地消失在以太中。

我们的> 在这里做的是从AJAX编码技术中取出叶子,并使用回调函数(在此示例中,我称为“ oncomplete”):

>
<form action=""> <br>
  <fieldset> <br>
    <input type="button" value="test1" onclick="test1()" /> <br>
    <input type="text"  /> <br>
  </fieldset> <br>
</form> <br>
登录后复制

因此,当我们调用checkSomething()时,我们将匿名函数作为其参数,并且当作业完成时,该函数以最终值调用:

>
function test2() <br>
{ <br>
  var result2 = document.getElementById('result2'); <br>
   <br>
  var start = new Date().getTime(); <br>
   <br>
  var i = 0, limit = 200, busy = false; <br>
  var processor = setInterval(function() <br>
  { <br>
    if(!busy) <br>
    { <br>
      busy = true; <br>
       <br>
      result2.value =  'time=' +  <br>
        (new Date().getTime() - start) + ' [i=' + i + ']'; <br>
       <br>
      process(); <br>
       <br>
      if(++i == limit) <br>
      { <br>
        clearInterval(processor); <br>
 <br>
        result2.value = 'time=' +  <br>
          (new Date().getTime() - start) + ' [done]'; <br>
      } <br>
       <br>
      busy = false; <br>
    } <br>
     <br>
  }, 100); <br>
   <br>
}
登录后复制

优雅?不,但是功能强大吗?是的。这就是重点。使用此技术,我们可以编写原本不可能的脚本。

>

android shove silicon绵羊的梦想?

>

>在我们的套件中,我们现在有一种方法来解决以前摆脱可能性领域的JavaScript项目。我开发了这种模式的游戏具有相当简单的逻辑,因此一个相当简单的brain>,但对于常规迭代而言,它仍然太多了。还有很多其他游戏需要更多的影响力!

>

我的下一个计划是使用此技术实现JavaScript国际象棋引擎。国际象棋具有各种可能的场景和战术,导致决策可能需要很长时间才能计算,远远超过没有这种技术的可行性。即使创建最基本的思维机器也需要进行激烈的计算,我承认对可能性感到非常兴奋。

>

如果我们能像这样删除技巧,谁能说出可能?自然语言处理,启发式方法……也许我们有在JavaScript中开发人工智能的基础!

>如果您喜欢阅读这篇文章,您会喜欢学习的;从大师那里学习新鲜技能和技术的地方。成员可以立即访问SitePoint的所有电子书和交互式在线课程,例如Web的JavaScript编程。 >对本文的评论已关闭。有关于JavaScript的疑问吗?为什么不在我们的论坛上询问? 图片来源:Randen l Peterson > Web工作人员在JavaScript多读力上的作用是什么?它们是Web内容在背景线程中运行脚本的简单手段。工作线程可以执行任务而不会干扰用户界面。此外,它们可以使用XMLHTTPRequest执行I/O(尽管响应XML和通道属性始终为null)。创建后,工人可以将消息发送到JavaScript代码,该消息通过将消息发布给该代码指定的事件处理程序(反之亦然)。 JavaScript如何处理多线程,尽管是单线读取?>

> javascript中多线程的限制是什么? 🎜>虽然可以通过Web Worker来实现JavaScript中的多线程,但重要的是要注意,这些工人无法访问DOM或其​​他Web API。它们仅限于几种可以来回发送到主线程的数据类型。另外,每个工人都是一个单独的实例,因此他们不共享范围或任何全局变量。

>为什么javascript支持多本身?

javascript被设计为避免复杂性和潜在的单线阅读数据操纵问题。多线程可能导致诸如种族条件之类的问题,其中输出取决于其他不可控制事件的序列或时间。据认为,优化单线读取环境会更容易,因为无需处理线程之间的依赖项。

>如何在JavaScript中使用Web Worker进行多线程?

>在JavaScript中使用Web Worker进行多线程,您需要创建一个新的Worker对象并指定在工作线程中执行要执行的JavaScript文件。然后,您可以使用tostmessage方法与工作线程进行通信,并使用onMessage事件处理程序从其接收消息。

>多插图可以使我的JavaScript代码更快?

多插图可能会使您的JavaScript代码可能使您的JavaScript代码。更快,但这取决于要执行的任务的性质。对于CPU密集型任务,多线程可以通过允许并行执行任务来显着提高性能。但是,对于I/O结合的任务,多线程的好处不太明显,因为这些任务通常受到CPU控制以外的因素的限制,例如网络速度。

>多线程和异步之间有什么区别在JavaScript?

>多线程和异步编程中的编程是否是同时管理多个任务的技术。但是,他们以不同的方式做到这一点。多线程涉及多个执行线程,每个线程执行不同的任务。另一方面,异步编程涉及单个执行线程,但是可以启动任务,然后暂停以稍后完成,从而允许在此期间执行其他任务。 JavaScript中的线程之间的数据共享?

> javaScript中线程之间的数据共享可以使用共享arraybuffer和Atomics实现。 sharedArrayBuffer允许在主线程和工作线程之间共享内存,而Atomics则提供了在共享内存上执行安全的原子操作的方法。

我可以在JavaScript中使用多线程进行前端开发吗? ,您可以在JavaScript中使用多线程进行前端开发。但是,重要的是要注意,启用多线程的Web工作人员无法访问DOM或其​​他Web API。因此,它们通常用于不涉及操纵DOM或与网页进行交互的任务,例如执行计算或处理数据。

以上是JavaScript中的多线程的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板