JavaScript 有没有办法实现多线程?

有帮助吗?

解决方案

http://caniuse.com/#search=worker 获取最新的支持信息。

以下是 2009 年左右的支持状况。


您想在谷歌上搜索的词是 JavaScript 工作线程

除了从 齿轮 目前还没有任何可用的信息,但是有很多关于如何实现这一点的讨论,所以我想请注意这个问题,因为答案无疑会在未来发生变化。

以下是 Gears 的相关文档: 工作池API

WHATWG 有一个针对工作线程的建议草案: 网络工作者

还有 Mozilla 的 DOM 工作线程


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

火狐3.5 有网络工作者。一些网络工作者的演示,如果你想看看它们的实际效果:

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

狩猎4, ,以及 WebKit 晚间节目 有工作线程:

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

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

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

其他提示

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

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

有一些奇怪的方法可以模拟异步执行 屈服, setTimeout(), setInterval(), XMLHttpRequest 或者 事件处理程序 (请参阅本文末尾的示例 屈服setTimeout()).

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


真正的多线程

多线程:JavaScript 工作线程

HTML5 引入了 Web Worker Threads(参见: 浏览器兼容性)
笔记:IE9及之前版本不支持。

这些工作线程是在后台运行的 JavaScript 线程,不会影响页面的性能。欲了解更多信息 网络工作者 阅读文档 或者 本教程.

下面是一个简单的示例,其中有 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, ,每一个都会执行一个线程。我们可以给 内嵌框架 一些参数由 URL 和 内嵌框架 可以与他的父母沟通以获得结果并将其打印回来( 内嵌框架 必须在同一域中)。

此示例并不适用于所有浏览器! 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 并发

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

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

生成器允许您暂停函数的执行并稍后恢复它。生成器可用于通过称为的技术来调度您的函数 蹦床.

这是示例:

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>

使用 HTML5“侧面规范” 不再需要破解 javascript 使用 setTimeout()、setInterval() 等。

HTML5 & Friends 引入了 javascript 网络工作者 规格。它是一个用于异步且独立运行脚本的 API。

链接至 规格 和一个 教程.

JavaScript 中不存在真正的线程。JavaScript 作为一种可塑性语言,确实允许您模拟其中的一些内容。这是一个 例子 前几天我遇到了。

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

您到底想实现什么目标?

这只是一种方法 模拟 JavaScript 中的多线程

现在我将创建 3 个线程来计算数字加法,数字可以除以 13,数字可以除以 3 直到 10000000000。而且这 3 个函数不能像并发一样同时运行。但我将向您展示一个技巧,使这些函数同时递归运行: jsFiddle

这段代码属于我。

身体的一部分

    <div class="div1">
    <input type="button" value="start/stop" onclick="_thread1.control ? _thread1.stop() : _thread1.start();" /><span>Counting summation of numbers till 10000000000</span> = <span id="1">0</span>
</div>
<div class="div2">
    <input type="button" value="start/stop" onclick="_thread2.control ? _thread2.stop() : _thread2.start();" /><span>Counting numbers can be divided with 13 till 10000000000</span> = <span id="2">0</span>
</div>
<div class="div3">
    <input type="button" value="start/stop" onclick="_thread3.control ? _thread3.stop() : _thread3.start();" /><span>Counting numbers can be divided with 3 till 10000000000</span> = <span id="3">0</span>
</div>

JavaScript 部分

var _thread1 = {//This is my thread as object
    control: false,//this is my control that will be used for start stop
    value: 0, //stores my result
    current: 0, //stores current number
    func: function () {   //this is my func that will run
        if (this.control) {      // checking for control to run
            if (this.current < 10000000000) {
                this.value += this.current;   
                document.getElementById("1").innerHTML = this.value;
                this.current++;
            }
        }
        setTimeout(function () {  // And here is the trick! setTimeout is a king that will help us simulate threading in javascript
            _thread1.func();    //You cannot use this.func() just try to call with your object name
        }, 0);
    },
    start: function () {
        this.control = true;   //start function
    },
    stop: function () {
        this.control = false;    //stop function
    },
    init: function () {
        setTimeout(function () {
            _thread1.func();    // the first call of our thread
        }, 0)
    }
};
var _thread2 = {
    control: false,
    value: 0,
    current: 0,
    func: function () {
        if (this.control) {
            if (this.current % 13 == 0) {
                this.value++;
            }
            this.current++;
            document.getElementById("2").innerHTML = this.value;
        }
        setTimeout(function () {
            _thread2.func();
        }, 0);
    },
    start: function () {
        this.control = true;
    },
    stop: function () {
        this.control = false;
    },
    init: function () {
        setTimeout(function () {
            _thread2.func();
        }, 0)
    }
};
var _thread3 = {
    control: false,
    value: 0,
    current: 0,
    func: function () {
        if (this.control) {
            if (this.current % 3 == 0) {
                this.value++;
            }
            this.current++;
            document.getElementById("3").innerHTML = this.value;
        }
        setTimeout(function () {
            _thread3.func();
        }, 0);
    },
    start: function () {
        this.control = true;
    },
    stop: function () {
        this.control = false;
    },
    init: function () {
        setTimeout(function () {
            _thread3.func();
        }, 0)
    }
};

_thread1.init();
_thread2.init();
_thread3.init();

我希望这种方式会有帮助。

你可以使用 叙述性 JavaScript, ,一个编译器,会将您的代码转换为状态机,从而有效地允许您模拟线程。它通过向语言添加一个“yielding”运算符(表示为“->”)来实现这一点,该运算符允许您在单个线性代码块中编写异步代码。

全新 v8 发动机 应该出来 今天支持它(我认为)

另一种可能的方法是在 javascript 环境中使用 javascript 解释器。

通过创建多个解释器并从主线程控制它们的执行,您可以模拟多线程,每个线程都在自己的环境中运行。

该方法有点类似于 Web Workers,但您允许解释器访问浏览器全局环境。

我做了一个小项目 证明这一点.

更详细的解释在 这篇博文.

在原始 Javascript 中,您能做的最好的事情就是使用少量异步调用 (xmlhttprequest),但这并不是真正的线程,而且非常有限。 谷歌齿轮 向浏览器添加了许多 API,其中一些可用于线程支持。

如果您不能或不想使用任何 AJAX 内容,请使用一个或十个 iframe!;) 您可以让进程在 iframe 中与母版页并行运行,而不必担心跨浏览器的可比较问题或 dot net AJAX 等语法问题,并且您可以从iframe。

例如,在父 iframe 中,调用 egFunction() iframe 内容加载后在父文档中(这是异步部分)

parent.egFunction();

如果您愿意,也可以动态生成 iframe,这样主要的 html 代码就可以不受它们的影响。

JavaScript 没有线程,但我们有工作线程。

如果您不需要共享对象,那么 Worker 可能是一个不错的选择。

大多数浏览器实现实际上会将工作线程分布在所有核心上,从而允许您利用所有核心。你可以看一下这个的演示 这里.

我开发了一个名为 任务.js 这使得这很容易做到。

任务.js 简化的界面,让 CPU 密集型代码在所有核心(node.js 和 Web)上运行

一个例子是

function blocking (exampleArgument) {
    // block thread
}

// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);

// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
    // do something with result
});

使用 HTML5 规格 你不需要为此编写太多 JS 或找到一些 hack。

HTML5 中引入的功能之一是 网络工作者 这是JavaScript在后台运行,独立于其他脚本,不影响页面的性能。

几乎所有浏览器都支持它:

铬 - 4.0+

IE - 10.0+

火狐 - 3.5+

Safari - 4.0+

歌剧 - 11.5+

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top