JavaScript é garantido para ser single-threaded?

JavaScript é conhecido por ser single-threaded em todas as implementações de navegador modernas, mas é que especificado em qualquer padrão ou é apenas por tradição? É totalmente seguro assumir que o JavaScript é sempre single-threaded?

Esta é uma boa pergunta. Eu adoraria dizer “sim”. Eu não posso.

Geralmente, considera-se que o JavaScript tenha um único encadeamento de execução visível para os scripts (*), de modo que quando seu script embutido, ouvinte de evento ou tempo limite for inserido, você permanecerá completamente no controle até retornar do final de seu bloco ou function.

(*: ignorando a questão de se os navegadores realmente implementam seus mecanismos JS usando um encadeamento OS, ou se outros encadeamentos de execução limitados são introduzidos pelos WebWorkers.)

No entanto, na realidade isso não é bem verdade , de formas sorrateiramente desagradáveis.

O caso mais comum é events imediatos. Os navegadores os triggersrão imediatamente quando seu código fizer algo para causá-los:

   

Resultados no log in, blur, log out em todos, exceto o IE. Esses events não são acionados apenas porque você chamou o focus() diretamente, eles podem acontecer porque você chamou alert() ou abriu uma janela pop-up ou qualquer outra coisa que mova o foco.

Isso também pode resultar em outros events. Por exemplo, adicione um ouvinte i.onchange e digite algo na input antes que a chamada focus() log in, change, blur, log out e a ordem de log in, change, blur, log out seja log in, change, blur, log out , exceto no Opera onde está log in, blur, log out, change e IE onde é (ainda menos explicável) log in, change, log out, blur .

Da mesma forma chamando click() em um elemento que fornece chama o manipulador onclick imediatamente em todos os navegadores (pelo menos isso é consistente!).

(Eu estou usando as propriedades diretas on... manipulador de events on... aqui, mas o mesmo acontece com addEventListener e attachEvent .)

Há também um monte de circunstâncias em que os events podem triggersr enquanto seu código é inserido, apesar de você não ter feito nada para provocá-lo. Um exemplo:

    

Bata alert e você terá uma checkbox de modal dialog. Nenhum outro script é executado até você ignorar esse diálogo, sim? Não. Redimensione a janela principal e você ficará alert in, resize, alert out na área de texto.

Você pode pensar que é impossível resize uma janela enquanto uma checkbox de modal dialog está ativa, mas não é assim: no Linux, você pode resize a janela o quanto quiser; no Windows não é tão fácil, mas você pode fazer isso alterando a resolução da canvas de uma maior para uma menor, onde a janela não cabe, fazendo com que ela seja redimensionada.

Você pode pensar, bem, é apenas resize (e provavelmente um pouco mais como scroll ) que pode triggersr quando o usuário não tem interação ativa com o navegador porque o script é encadeado. E para janelas simples você pode estar certo. Mas tudo isso vai para o pote assim que você estiver fazendo scripts entre janelas. Para todos os navegadores, exceto o Safari, que bloqueia todas as janelas / guias / frameworks quando qualquer um deles está ocupado, você pode interagir com um documento a partir do código de outro documento, executando em um thread separado de execução e fazendo com que todos os manipuladores de events relacionados fogo.

Locais em que os events que você pode gerar podem ser gerados enquanto o script ainda está em execução:

  • quando os popups modais ( alert , confirm , prompt ) estão abertos, em todos os navegadores, exceto o Opera;

  • durante showModalDialog em navegadores que o suportam;

  • a checkbox de diálogo “Um script nesta página pode estar ocupado …”, mesmo se você optar por deixar o script continuar a ser executado, permite que events como resize e desfoque sejam acionados e ser manipulados mesmo enquanto o script está no meio de um ocupado-loop, exceto no Opera.

  • Há um tempo atrás, para mim, no IE com o Sun Java Plugin, chamar qualquer método em um applet poderia permitir que os events triggersssem e o script fosse reinserido. Este foi sempre um bug sensível ao tempo, e é possível que a Sun o tenha corrigido desde então (eu certamente espero que sim).

  • provavelmente mais. Já faz um tempo desde que eu testei isso e navegadores ganharam complexidade desde então.

Em resumo, o JavaScript aparece para a maioria dos usuários, na maioria das vezes, para ter um único segmento de execução orientado a events. Na realidade, não tem tal coisa. Não está claro o quanto isso é simplesmente um bug e quanto design deliberado, mas se você está escrevendo aplicativos complexos, especialmente cross-window / frame-scripting, há todas as chances de que ele poderia morder – e em intermitente, maneiras difíceis de depurar.

Se o pior acontecer, você poderá resolver problemas de simultaneidade indiretamente por todas as respostas de events. Quando um evento chegar, solte-o em uma fila e lide com a fila na ordem posterior, em uma function setInterval . Se você está escrevendo um framework que pretende usar em aplicações complexas, fazer isso pode ser uma boa jogada. postMessage também irá, esperamos, aliviar a dor dos scripts entre documentos no futuro.

Eu diria sim – porque praticamente todo o código javascript existente (pelo menos todo não trivial) seria quebrado se o mecanismo de javascript de um navegador fosse executá-lo de forma assíncrona.

Acrescente a isso o fato de que HTML5 já especifica Web Workers (uma API padronizada e explícita para código javascript multi-threading) introduzindo multi-threading no Javascript básico, seria inútil.

( Nota para outros comentadores: Mesmo que setTimeout/setInterval , events de onload de solicitação de HTTP (XHR) e events de interface do usuário (clique, foco, etc.) forneçam uma impressão crua de multiencadeamento – eles ainda são todos executados ao longo de uma única linha do tempo – um de cada vez – portanto, mesmo que não saibamos a ordem de execução deles de antemão, não há necessidade de nos preocupar com as condições externas que mudam durante a execução de um manipulador de events, function cronometrada ou retorno de chamada XHR.

Sim, embora você ainda possa sofrer alguns dos problemas de programação simultânea (principalmente condições de corrida) ao usar qualquer uma das APIs assíncronas, como callbacks setInterval e xmlhttp.

Sim, embora o Internet Explorer 9 compile seu Javascript em um thread separado em preparação para execução no thread principal. Isso não muda nada para você como programador, no entanto.

Na verdade, uma janela pai pode se comunicar com janelas ou frameworks filhos ou irmãos que possuem seus próprios encadeamentos de execução em execução.

JavaScript / ECMAScript é projetado para viver em um ambiente de host. Ou seja, o JavaScript não faz nada a menos que o ambiente do host decida analisar e executar um determinado script e fornecer objects de ambiente que permitam que o JavaScript seja realmente útil (como o DOM nos navegadores).

Eu acho que uma determinada function ou bloco de script será executado linha por linha e que é garantido para JavaScript. No entanto, talvez um ambiente host possa executar vários scripts ao mesmo tempo. Ou, um ambiente host sempre poderia fornecer um object que fornece multi-threading. setTimeout e setInterval são exemplos, ou pelo menos pseudo-exemplos, de um ambiente de host que fornece uma maneira de fazer alguma concorrência (mesmo que não seja exatamente uma simultaneidade).

Eu diria que a especificação não impede que alguém crie um mecanismo que execute javascript em vários threads, exigindo que o código execute a synchronization para acessar o estado do object compartilhado.

Eu acho que o paradigma single-threaded non-blocking surgiu da necessidade de executar o javascript em navegadores onde o ui nunca deveria ser bloqueado.

O Nodejs seguiu a abordagem dos navegadores.

O mecanismo Rhino , no entanto, suporta a execução do código js em diferentes threads . As execuções não podem compartilhar o contexto, mas podem compartilhar o escopo. Para este caso específico, a documentação declara:

… “A Rhino garante que accesss a propriedades de objects JavaScript são atômicos em encadeamentos, mas não oferece mais garantias para scripts sendo executados no mesmo escopo ao mesmo tempo. Se dois scripts usam o mesmo escopo simultaneamente, os scripts são responsável por coordenar quaisquer accesss a variables ​​compartilhadas . ”

A partir da leitura da documentação do Rhino, concluo que é possível que alguém escreva uma API de javascript que também crie novos encadeamentos de javascript, mas a API seria específica de rinoceronte (por exemplo, o nó só pode gerar um novo processo).

Eu imagino que mesmo para um mecanismo que suporte vários threads em JavaScript, deve haver compatibilidade com scripts que não considerem multi-threading ou bloqueio.

Concendo navegadores e nodejs do jeito que eu vejo é:

  • Todo o código js é executado em um único encadeamento? : Sim.

  • O código js pode fazer com que outros threads sejam executados? : Sim.

  • Esses threads podem sofrer mutação no contexto de execução js ?: Não. Mas eles podem (direta / indiretamente (?)) Anexar à fila de events.

Portanto, no caso de navegadores e nodejs (e provavelmente muitos outros mecanismos), o javascript não é multithreaded, mas os mecanismos em si são.

Não.

Eu estou indo contra a multidão aqui, mas tenha paciência comigo. Um único script JS destina-se a ser efetivamente com encadeamento único, mas isso não significa que ele não possa ser interpretado de forma diferente.

Digamos que você tenha o seguinte código …

 var list = []; for (var i = 0; i < 10000; i++) { list[i] = i * i; } 

Isso é escrito com a expectativa de que, no final do loop, a lista deve ter 10000 inputs, que são o índice ao quadrado, mas a VM pode perceber que cada iteração do loop não afeta o outro e reinterpretá-lo usando dois threads.

Primeiro segmento

 for (var i = 0; i < 5000; i++) { list[i] = i * i; } 

Segunda linha

 for (var i = 5000; i < 10000; i++) { list[i] = i * i; } 

Estou simplificando aqui, porque os arrays JS são mais complicados do que pedaços idiotas de memory, mas se esses dois scripts são capazes de adicionar inputs ao array de uma maneira segura para thread, então, quando ambos forem executados, ele terá o mesmo resultado que a versão single-threaded.

Embora eu não esteja ciente de nenhuma VM que esteja detectando código paralelizável como esse, parece provável que ele possa vir a existir no futuro para VMs JIT, já que ele poderia oferecer mais velocidade em algumas situações.

Levando esse conceito adiante, é possível que o código possa ser anotado para permitir que a VM saiba o que converter em código multiencadeado.

 // like "use strict" this enables certain features on compatible VMs. "use parallel"; var list = []; // This string, which has no effect on incompatible VMs, enables threading on // this loop. "parallel for"; for (var i = 0; i < 10000; i++) { list[i] = i * i; } 

Já que os Web Workers estão chegando ao Javascript, é improvável que esse sistema mais feio venha a existir, mas acho que é seguro dizer que o Javascript é single-threaded pela tradição.

@Bobince está fornecendo uma resposta realmente opaca.

Riffing fora da resposta de Már Örlygsson, o Javascript é sempre single-threaded por causa deste simples fato: Tudo em JavaScript é executado ao longo de uma única linha do tempo.

Essa é a definição estrita de uma linguagem de programação single-threaded.

Bem, o Chrome é multiprocesso, e acho que todo processo lida com seu próprio código Javascript, mas, até onde o código sabe, ele é “single-threaded”.

Não existe qualquer suporte em Javascript para multi-threading, pelo menos não explicitamente, por isso não faz diferença.

Eu tentei o exemplo do @ bobince com pequenas modificações:

   Test    

Então, quando você aperta Run, fecha o popup de alerta e faz um “thread único”, você deve ver algo assim:

 click begin click end result = 20, should be 20 

Mas se você tentar rodar isso no Opera ou Firefox estável no Windows e minimizar / maximizar janela com popup de alerta na canvas, então haverá algo assim:

 click begin resize click end result = 15, should be 20 

Eu não quero dizer que isso é “multithreading”, mas algum pedaço de código foi executado em um momento errado comigo não esperando isso, e agora eu tenho um estado corrompido. E melhor saber sobre esse comportamento.

Tente aninhar duas funções setTimeout umas dentro das outras e elas se comportarão com multithread (isto é, o timer externo não esperará que o interno seja concluído antes de executar sua function).