Pergunta

Existe alguma maneira de fazer multithreading em JavaScript?

Foi útil?

Solução

Ver http://caniuse.com/#search=worker para obter as informações de suporte mais atualizadas.

A seguir estava o estado do suporte por volta de 2009.


As palavras que você deseja pesquisar no Google são Threads de trabalho JavaScript

Além de Engrenagens não há nada disponível no momento, mas há muita conversa sobre como implementar isso, então acho que observe esta pergunta, pois a resposta sem dúvida mudará no futuro.

Aqui está a documentação relevante para o Gears: API WorkerPool

WHATWG tem um rascunho de recomendação para threads de trabalho: Trabalhadores da Web

E também tem o Mozilla Threads de trabalho DOM


Atualizar: Junho de 2009, estado atual do suporte do navegador para threads JavaScript

Firefox 3.5 tem trabalhadores da web.Algumas demonstrações de web workers, se você quiser vê-los em ação:

O plugin Gears também pode ser instalado no Firefox.

Safári 4, e a Noites do WebKit tem threads de trabalho:

cromada tem o Gears integrado, para que possa executar threads, embora exija um prompt de confirmação do usuário (e use uma API diferente para web workers, embora funcione em qualquer navegador com o plugin Gears instalado):

IE8 e IE9 só pode fazer threads com o plugin Gears instalado

Outras dicas

Maneira diferente de fazer multithreading e assíncrono em JavaScript

Antes do HTML5, o JavaScript permitia apenas a execução de um thread por página.

Havia alguma maneira hackeada de simular uma execução assíncrona com Colheita, setTimeout(), setInterval(), XMLHttpRequest ou manipuladores de eventos (veja no final deste post um exemplo com colheita e setTimeout()).

Mas com HTML5 agora podemos usar Worker Threads para paralelizar a execução de funções.Aqui está um exemplo de uso.


Multithreading real

Multiencadeamento:Threads de trabalho JavaScript

HTML5 introduziu Web Worker Threads (veja: compatibilidade de navegadores)
Observação:O IE9 e versões anteriores não oferecem suporte.

Esses threads de trabalho são threads JavaScript executados em segundo plano sem afetar o desempenho da página.Para mais informações sobre Trabalhador da Web leia a documentação ou este tutorial.

Aqui está um exemplo simples com 3 threads do Web Worker que contam até MAX_VALUE e mostram o valor computado atual em nossa página:

//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>

Podemos ver que os três threads são executados simultaneamente e imprimem seu valor atual na página.Eles não congelam a página porque são executados em segundo plano com threads separados.


Multiencadeamento:com vários iframes

Outra maneira de conseguir isso é usar vários iframes, cada um executará um thread.Podemos dar o iframe alguns parâmetros pelo URL e pelo iframe pode se comunicar com seus pais para obter o resultado e imprimi-lo (o iframe deve estar no mesmo domínio).

Este exemplo não funciona em todos os navegadores! iframes geralmente são executados no mesmo thread/processo da página principal (mas o Firefox e o Chromium parecem lidar com isso de maneira diferente).

Como o trecho de código não suporta vários arquivos HTML, fornecerei apenas os diferentes códigos aqui:

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);
    }
})();

Simular multithreading

Thread único:emular simultaneidade JavaScript com setTimeout()

A maneira 'ingênua' seria executar a função setTimeout() um após o outro assim:

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

Mas este método não funciona porque cada tarefa será executada uma após a outra.

Podemos simular a execução assíncrona chamando a função recursivamente assim:

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>

Como você pode ver este segundo método é muito lento e congela o navegador porque usa o thread principal para executar as funções.


Thread único:emular simultaneidade JavaScript com rendimento

Colheita é um novo recurso em ECMAScript 6, funciona apenas na versão mais antiga do Firefox e Chrome (no Chrome você precisa ativar JavaScript experimental aparecendo em chrome://flags/#enable-javascript-harmony).

A palavra-chave yield faz com que a execução da função do gerador seja pausada e o valor da expressão após a palavra-chave yield seja retornado ao chamador do gerador.Pode ser pensado como uma versão baseada em gerador da palavra-chave return.

Um gerador permite suspender a execução de uma função e retomá-la mais tarde.Um gerador pode ser usado para agendar suas funções com uma técnica chamada trampolim.

Aqui está o exemplo:

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>

Com as "especificações secundárias" do HTML5 não há mais necessidade de hackear javascript com setTimeout(), setInterval(), etc.

HTML5 & Friends apresenta o javascript Trabalhadores da Web especificação.É uma API para executar scripts de forma assíncrona e independente.

Links para o especificação e um tutorial.

Não há threading verdadeiro em JavaScript.Sendo o JavaScript a linguagem maleável que é, permite emular algumas delas.Aqui está um exemplo Me deparei outro dia.

Não existe multithreading verdadeiro em Javascript, mas você pode obter comportamento assíncrono usando setTimeout() e solicitações AJAX assíncronas.

O que exatamente você está tentando realizar?

Aqui está apenas uma maneira de simular multithreading em Javascript

Agora vou criar 3 threads que irão calcular a adição de números, os números podem ser divididos por 13 e os números podem ser divididos por 3 até 10000000000.E essas 3 funções não podem ser executadas ao mesmo tempo que o que significa Simultaneidade.Mas vou mostrar um truque que fará com que essas funções sejam executadas recursivamente ao mesmo tempo: jsFiddle

Este código pertence a mim.

Parte do corpo

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

Parte 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();

Espero que esta forma seja útil.

Você poderia usar JavaScript narrativo, um compilador que transformará seu código em uma máquina de estado, permitindo efetivamente emular threading.Isso é feito adicionando um operador "yielding" (notado como '->') à linguagem que permite escrever código assíncrono em um único bloco de código linear.

O novo motor v8 que deveria sair hoje apoia (eu acho)

Outro método possível é usar um interpretador javascript no ambiente javascript.

Ao criar vários intérpretes e controlar sua execução a partir do thread principal, você pode simular multithreading com cada thread sendo executado em seu próprio ambiente.

A abordagem é um pouco semelhante aos web workers, mas você dá ao intérprete acesso ao ambiente global do navegador.

Fiz um pequeno projeto para demonstre isso.

Uma explicação mais detalhada em esta postagem do blog.

Em Javascript bruto, o melhor que você pode fazer é usar algumas chamadas assíncronas (xmlhttprequest), mas isso não é realmente threading e é muito limitado. Google Engrenagens adiciona uma série de APIs ao navegador, algumas das quais podem ser usadas para suporte a threading.

Se você não pode ou não quer usar nenhum material AJAX, use um iframe ou dez!;) Você pode ter processos em execução em iframes em paralelo com a página mestra sem se preocupar com problemas comparáveis ​​entre navegadores ou problemas de sintaxe com dot net AJAX etc., e você pode chamar o JavaScript da página mestra (incluindo o JavaScript que ela importou) de um iframe.

Por exemplo, em um iframe pai, para chamar egFunction() no documento pai depois que o conteúdo do iframe for carregado (essa é a parte assíncrona)

parent.egFunction();

Gere dinamicamente os iframes também para que o código HTML principal fique livre deles, se desejar.

Javascript não possui threads, mas temos trabalhadores.

Os trabalhadores podem ser uma boa escolha se você não precisar de objetos compartilhados.

A maioria das implementações de navegador irá, na verdade, distribuir trabalhadores por todos os núcleos, permitindo que você utilize todos os núcleos.Você pode ver uma demonstração disso aqui.

Eu desenvolvi uma biblioteca chamada tarefa.js isso torna isso muito fácil de fazer.

tarefa.js Interface simplificada para fazer com que código com uso intensivo de CPU seja executado em todos os núcleos (node.js e web)

Um exemplo seria

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
});

Com HTML5 especificação você não precisa escrever muito JS para o mesmo ou encontrar alguns hacks.

Um dos recursos introduzidos no HTML5 é Trabalhadores da Web que é o JavaScript rodando em segundo plano, independentemente de outros scripts, sem afetar o desempenho da página.

É compatível com quase todos os navegadores:

Cromo - 4.0+

Ou seja - 10,0+

Mozilla-3.5+

Safári - 4.0+

Ópera - 11,5+

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top