JavaScript 是多线程的吗?

64

这是我的问题 - 我需要使用jQuery.getScript()动态下载多个脚本,并在所有脚本加载完成后执行特定的JavaScript代码,因此我的计划是像这样做:

function GetScripts(scripts, callback)
{
  var len = scripts.length
  for (var i in scripts)
  {
    jQuery.getScript(scripts[i], function() 
    {
      len --;
      // executing callback function if this is the last script that loaded
      if (len == 0)
        callback()  
    })
  }
}

如果我们假设每个脚本的 script.onload 事件按顺序同步执行,那么这种方法才能可靠地工作。因此,在任何情况下,两个或更多的事件处理程序都不会通过 (len == 0) 的检查并执行回调方法。

我的问题是 - 这种假设是否正确?如果不正确,有什么方法可以实现我想要做的事情?


1
JavaScript 是多线程的,这里有一个链接可以查看示例:http://ajaxian.com/archives/multi-threaded-javascript - Anthony Forloney
6
@aforloney - 这并不完全正确。正如文章中引用的太阳工程师所说,Rhino JavaScript引擎“允许Java方法调用。因此,我们可以使用它来为Java平台类创建脚本包装器...我们可以轻松地在脚本中创建线程。” - Jeff Sternal
async.js非常适合在所有脚本运行完成后执行代码,无论是并行还是顺序运行。 - Maxim Krizhanovsky
9个回答

88
不,JavaScript不支持多线程。它是基于事件驱动的,因此你会看到事件按顺序依次触发(假设它们按顺序加载)。你当前的实现似乎是正确的。我相信jQuery的.getScript()会注入一个新的<script> 标签,这样它们也应该按正确的顺序加载。

我基本上不关心加载的顺序,我只需要确保我的回调函数在所有脚本都加载完成后才会执行,因为它依赖于它们全部加载完成。 - Andrey
9
这可能是一个令人尴尬的简单问题,但如果JavaScript不支持多线程,为什么多个串联的jQuery动画(例如)看起来同时进行而不是按顺序进行呢? - Larry Lustig
12
动画更新是由定时器事件触发的。 - Jacob
12
据我所了解,动画使用定时器,每个动画不是单一的进程,而是在某个定时器事件上进行一系列小的增量变化,因此似乎多个动画同时发生。 - Andrey

36

目前JavaScript不支持多线程,但是这种情况将在不久的将来发生改变。在HTML5中有一个新功能叫做Worker。它允许您在后台执行某些任务。

但是它目前并不被所有浏览器支持。


5
Web Workers是HTML5规范的一部分。 http://www.whatwg.org/specs/web-workers/current-work/ - Rob
8
关于 Workers 需要注意的两件事是它们无法访问 DOM,也不能访问父页面。 - Seanny123
1
现在所有新的浏览器都支持Web Workers(IE 10是最后一个加入的)。http://www.w3schools.com/html/html5_webworkers.asp - CodeMonkey
这篇原始帖子已经过时了吗?多年来没有任何更新。我想知道,如果JavaScript不支持多线程,那么像异步调用、事件监听器、Promises等等的东西是如何工作的呢?是什么在管理这些异步/并行事件?更合适的思考方式是将单个JS调用堆栈视为单线程,但浏览器中的大量异步JS操作作为由DOM控制的“线程”集合吗? - h0r53
此外,如果我在浏览器开发者控制台中设置了断点,为什么像间隔这样的东西仍然会执行?是否发生了上下文切换?是什么控制它? - h0r53
显示剩余4条评论

18

JavaScript (ECMAScript)规范没有定义任何线程或同步机制。

此外,我们浏览器中的JavaScript引擎故意是单线程的。一部分原因是允许超过一个UI线程并发操作将会导致巨大的问题。所以你的假设和实现是正确的。

另外,另一位评论者提到任何JavaScript引擎供应商都可以添加线程和同步特性,或者供应商可以使用户自行实现这些功能,正如这篇文章所述: 多线程JavaScript所述。


1
我总是对那些符合规范的答案印象深刻。顺便说一句,这是真心话,不是讽刺。 - Michael Easter

14

JavaScript绝对不是多线程的——您有保证,任何使用的处理程序都不会被另一个事件中断。任何其他事件,如鼠标单击、XMLHttpRequest返回和计时器在代码执行时将排队等待,并逐一运行。


11

不,所有浏览器都只为JavaScript提供一个线程。


3
+1,虽然他们本可以做出不同的选择,但那样做会引发混乱。 - Jeff Sternal

7
清楚地说,浏览器JS实现不支持多线程。 语言JS可以是多线程的。
然而,这个问题不适用于此处。
适用的是getScript()是异步的(立即返回并排队),但是,浏览器将按顺序执行附加在DOM上的<script>内容,因此您的依赖JS代码将按顺序加载。这是浏览器的特性,与JS线程或getScript()调用无关。
如果getScript()使用xmlHTTPRequest、setTimeout()、websockets或任何其他异步调用检索脚本,则不能保证您的脚本按顺序执行。但是,由于您的'len'变量的执行上下文位于闭包中,因此它通过异步调用您的函数保持其上下文,并且回调仍将在所有脚本执行后被调用。

4

1

想尝试使用“强制”延迟脚本交付,可能会很有趣...

  1. 添加了两个可用的Google脚本
  2. 将delayjs.php作为第二个数组元素添加。 delayjs.php在交付空js对象之前休眠5秒。
  3. 添加了一个回调函数,"验证"脚本文件中预期对象的存在。
  4. 添加了一些js命令,在GetScripts()调用后执行,以"测试"顺序js命令。

脚本加载的结果符合预期;只有在最后一个脚本加载完成后才触发回调。令我惊讶的是,在等待最后一个脚本加载时,跟随GetScripts()调用的js命令也会被触发执行。我原本以为在浏览器等待js脚本加载时不会执行任何js命令...

var scripts = [];
scripts.push('http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js');
scripts.push('http://localhost/delayjs.php');
scripts.push('http://ajax.googleapis.com/ajax/libs/scriptaculous/1.8.3/scriptaculous.js');


function logem() {
    console.log(typeof Prototype);
    console.log(typeof Scriptaculous);
    console.log(typeof delayedjs);
}

GetScripts( scripts, logem );

console.log('Try to do something before GetScripts finishes.\n');
$('#testdiv').text('test content');

<?php

sleep(5);
echo 'var delayedjs = {};';

2
不要把自己搞混了 - 浏览器在 JavaScript 线程之外下载脚本,因此 JavaScript 在等待脚本加载时继续执行。 - Andrey

0
你可以通过在 HTML 文档中创建多个帧,并在每个帧中运行一个脚本,每个脚本调用主帧中的一个函数来理解这些函数的结果,从而可能实现某种多线程功能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接