JavaScript 和线程

IT技术 javascript multithreading
2021-01-29 04:19:31

有没有办法在 JavaScript 中进行多线程处理?

6个回答

有关最新的支持信息,请参阅http://caniuse.com/#search=worker

以下是大约 2009 年的支持状态。


你想用谷歌搜索的词是JavaScript Worker Threads

除了Gears之外,现在没有可用的东西,但是有很多关于如何实现它的讨论,所以我想看看这个问题,因为答案无疑会在未来改变。

以下是 Gears 的相关文档:WorkerPool API

WHATWG 有一份针对工作线程的建议草案:Web Workers

还有 Mozilla 的DOM Worker Threads


更新: 2009 年 6 月,浏览器支持 JavaScript 线程的当前状态

Firefox 3.5有网络工作者。一些网络工作者的演示,如果你想看到他们的行动:

Gears 插件也可以安装在 Firefox 中。

Safari 4WebKit nightlies有工作线程:

Chrome内置了 Gears,因此它可以执行线程,尽管它需要用户的确认提示(并且它对网络工作者使用不同的 API,尽管它可以在安装了 Gears 插件的任何浏览器中使用):

  • Google Gears WorkerPool Demo(不是一个很好的例子,因为它运行速度太快而无法在 Chrome 和 Firefox 中进行测试,尽管 IE 运行它的速度足够慢,可以看到它阻止交互)

IE8IE9只能在安装了 Gears 插件的情况下执行线程

@SamHasler 您可能想修改您的答案。现在所有现代桌面浏览器都支持网络工作者。另见caniuse.com/#search=worker
2021-03-21 04:19:31
尽管 Safari 4 支持网络工作者,但似乎只有 Firefox 支持通过 postMessage 传递复杂对象:hacks.mozilla.org/2009/07/working-smarter-not-harder请参阅该帖子的最后一段有关 Bespin 项目中实际使用情况的内容用于根据 Google Gears 实现 Worker API 的 shim 的链接并将缺失的功能添加到 Safari 4 的 Worker 实现中,以及它们如何在 postMessage 接口之上实现透明自定义事件的详细信息。
2021-03-22 04:19:31
@SamHasler 还值得注意的是,不再支持 Google Gears。
2021-03-22 04:19:31
现在IE9出来了,你可以把“IE8只能在安装Gears插件的情况下做线程”更新为“IE8和IE9只能在安装了Gears插件的情况下做线程”
2021-04-03 04:19:31
@inf3rno 用于在另一个线程上进行冗长的计算,因此它们不会减慢浏览器 UI 的速度。
2021-04-07 04:19:31

在 JavaScript 中执行多线程和异步的不同方式

在 HTML5 之前,JavaScript 只允许每页执行一个线程。

有以模拟与异步执行一些哈克的方式产率setTimeout()setInterval()XMLHttpRequest事件处理程序(看到此信息的用于与例如端部收率setTimeout())。

但是在 HTML5 中,我们现在可以使用工作线程来并行化函数的执行。这是一个使用示例。


真正的多线程

多线程:JavaScript 工作线程

HTML5引入了 Web Worker Threads(参见:浏览器兼容性
注意:IE9 及更早版本不支持它。

这些工作线程是在后台运行而不影响页面性能的 JavaScript 线程。有关Web Worker 的 更多信息,请阅读文档本教程

这是一个简单的示例,其中包含 3 个 Web Worker 线程,这些线程计数到 MAX_VALUE 并在我们的页面中显示当前计算的值:

//As a worker normally take another JavaScript file to execute we convert the function in an URL: http://stackoverflow.com/a/16799132/2576706
function getScriptPath(foo){ return window.URL.createObjectURL(new Blob([foo.toString().match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1]],{type:'text/javascript'})); }

var MAX_VALUE = 10000;

/*
 *	Here are the workers
 */
//Worker 1
var worker1 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
//We add a listener to the worker to get the response and show it in the page
worker1.addEventListener('message', function(e) {
  document.getElementById("result1").innerHTML = e.data;
}, false);


//Worker 2
var worker2 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
worker2.addEventListener('message', function(e) {
  document.getElementById("result2").innerHTML = e.data;
}, false);


//Worker 3
var worker3 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
worker3.addEventListener('message', function(e) {
    document.getElementById("result3").innerHTML = e.data;
}, false);


// Start and send data to our worker.
worker1.postMessage(MAX_VALUE); 
worker2.postMessage(MAX_VALUE); 
worker3.postMessage(MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

我们可以看到三个线程是并发执行的,并在页面中打印了它们的当前值。它们不会冻结页面,因为它们是在后台以分离的线程执行的。


多线程:具有多个 iframe

实现此目的的另一种方法是使用多个iframe,每个iframe将执行一个线程。我们可以通过 URLiframe 提供一些参数,iframe可以与其父级通信以获取结果并将其打印回来(iframe必须在同一域中)。

此示例不适用于所有浏览器! iframe通常在与主页相同的线程/进程中运行(但 Firefox 和 Chromium 似乎以不同的方式处理它)。

由于代码片段不支持多个 HTML 文件,我将在此处提供不同的代码:

索引.html:

//The 3 iframes containing the code (take the thread id in param)
<iframe id="threadFrame1" src="thread.html?id=1"></iframe>
<iframe id="threadFrame2" src="thread.html?id=2"></iframe>
<iframe id="threadFrame3" src="thread.html?id=3"></iframe>

//Divs that shows the result
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>


<script>
    //This function is called by each iframe
    function threadResult(threadId, result) {
        document.getElementById("result" + threadId).innerHTML = result;
    }
</script>

线程.html:

//Get the parameters in the URL: http://stackoverflow.com/a/1099670/2576706
function getQueryParams(paramName) {
    var qs = document.location.search.split('+').join(' ');
    var params = {}, tokens, re = /[?&]?([^=]+)=([^&]*)/g;
    while (tokens = re.exec(qs)) {
        params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
    }
    return params[paramName];
}

//The thread code (get the id from the URL, we can pass other parameters as needed)
var MAX_VALUE = 100000;
(function thread() {
    var threadId = getQueryParams('id');
    for(var i=0; i<MAX_VALUE; i++){
        parent.threadResult(threadId, i);
    }
})();

模拟多线程

单线程:使用 setTimeout() 模拟 JavaScript 并发

“天真”的方法是setTimeout()像这样一个接一个地执行函数

setTimeout(function(){ /* Some tasks */ }, 0);
setTimeout(function(){ /* Some tasks */ }, 0);
[...]

但是这种方法不起作用,因为每个任务都会一个接一个地执行。

我们可以通过递归调用函数来模拟异步执行,如下所示:

var MAX_VALUE = 10000;

function thread1(value, maxValue){
    var me = this;
    document.getElementById("result1").innerHTML = value;
    value++;
  
    //Continue execution
    if(value<=maxValue)
        setTimeout(function () { me.thread1(value, maxValue); }, 0);
}

function thread2(value, maxValue){
    var me = this;
    document.getElementById("result2").innerHTML = value;
    value++;
	
    if(value<=maxValue)
        setTimeout(function () { me.thread2(value, maxValue); }, 0);
}

function thread3(value, maxValue){
    var me = this;
    document.getElementById("result3").innerHTML = value;
    value++;
	
    if(value<=maxValue)
        setTimeout(function () { me.thread3(value, maxValue); }, 0);
}

thread1(0, MAX_VALUE);
thread2(0, MAX_VALUE);
thread3(0, MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

正如您所看到的,第二种方法非常慢并且会冻结浏览器,因为它使用主线程来执行函数。


单线程:用 yield 模拟 JavaScript 并发

YieldECMAScript 6 中的一项新功能,它仅适用于最旧版本的 Firefox 和 Chrome(在 Chrome 中,您需要启用出现在chrome://flags/#enable-javascript-harmony 中的实验性 JavaScript)。

yield 关键字导致生成器函数执行暂停,并且 yield 关键字后面的表达式的值返回给生成器的调用者。它可以被认为是 return 关键字的基于生成器的版本。

生成器允许您暂停函数的执行并在以后恢复它。生成器可用于通过称为Trampolining的技术来安排您的功能

这是示例:

var MAX_VALUE = 10000;

Scheduler = {
	_tasks: [],
	add: function(func){
		this._tasks.push(func);
	},	
	start: function(){
		var tasks = this._tasks;
		var length = tasks.length;
		while(length>0){
			for(var i=0; i<length; i++){
				var res = tasks[i].next();
				if(res.done){
					tasks.splice(i, 1);
					length--;
					i--;
				}
			}
		}
	}	
}


function* updateUI(threadID, maxValue) {
  var value = 0;
  while(value<=maxValue){
	yield document.getElementById("result" + threadID).innerHTML = value;
	value++;
  }
}

Scheduler.add(updateUI(1, MAX_VALUE));
Scheduler.add(updateUI(2, MAX_VALUE));
Scheduler.add(updateUI(3, MAX_VALUE));

Scheduler.start()
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

这真的应该是最好的答案。
2021-03-24 04:19:31

使用 HTML5 “side-specs”不再需要使用 setTimeout()、setInterval() 等来破解 javascript

HTML5 & Friends 引入了 javascript Web Workers规范。它是一个用于异步和独立运行脚本的 API。

规范教程的链接

JavaScript 中没有真正的线程。JavaScript 是一种具有延展性的语言,它确实允许您模拟其中的一些。这是我前几天遇到的一个例子

“真正的线程”是什么意思?绿色线程是真正的线程。
2021-03-31 04:19:31

Javascript 中没有真正的多线程,但是您可以使用setTimeout()异步 AJAX 请求获得异步行为

你到底想完成什么?