为了账号安全,请及时绑定邮箱和手机立即绑定

JavaScript和线程

/ 猿问

JavaScript和线程

慕运维1137616 2019-09-19 09:51:46

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



查看完整描述

3 回答

?
HUX布斯

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


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


你想谷歌的词是JavaScript工作者线程


除了Gears之外,目前还没有什么可用,但是有很多关于如何实现这一点的讨论,所以我想看看这个问题,因为答案无疑会在未来发生变化。


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


WHATWG有一份关于工作者线程的建议草案:Web Workers


还有Mozilla的DOM Worker Threads


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


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


模拟退火(“试一试”链接)

太空入侵者(帖子末尾的链接)

MoonBat JavaScript Benchmark(第一个链接)

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


Safari 4和WebKit nightlies都有工作线程:


JavaScript Ray Tracer

Chrome有Gears,所以它可以做线程,虽然它需要用户的确认提示(它使用不同的API到Web工作者,虽然它可以在安装了Gears插件的任何浏览器中工作):


谷歌Gears WorkerPool演示(不是一个很好的例子,因为它运行速度太快,无法在Chrome和Firefox中进行测试,尽管IE运行得足够慢以至于阻止了交互)

IE8和IE9只能安装Gears插件的线程


查看完整回答
反对 回复 2019-09-19
?
狐的传说

在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将执行一个线程。我们可以通过URL 为iframe提供一些参数,iframe可以与其父级进行通信,以获得结果并将其打印回来(iframe必须位于同一个域中)。


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


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


index.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>

thread.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出现在镀铬://标志/#启用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>


查看完整回答
反对 回复 2019-09-19
?
123456qqq

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


HTML5和Friends介绍了javascript Web Workers规范。它是一个异步和独立运行脚本的API。


指向规范和教程的链接。


查看完整回答
反对 回复 2019-09-19

添加回答

回复

举报

0/150
提交
取消
意见反馈 邀请有奖 帮助中心 APP下载
官方微信