Sabe-se que o JavaScript possui um único encadeamento em todas as implementações modernas de navegadores, mas isso é especificado em algum padrão ou é apenas por tradição? É totalmente seguro assumir que o JavaScript é sempre de thread único?
javascript
concurrency
Egor Pavlikhin
fonte
fonte
Respostas:
Esta é uma boa pergunta. Eu adoraria dizer "sim". Não posso.
Geralmente, o JavaScript é considerado como tendo um único encadeamento de execução visível para scripts (*), para que, quando seu script embutido, ouvinte de evento ou tempo limite for digitado, você permaneça completamente no controle até retornar do final de seu bloco ou função.
(*: ignorando a questão de saber se os navegadores realmente implementam seus mecanismos JS usando um thread do SO ou se outros threads de execução limitados são introduzidos pelos WebWorkers.)
No entanto, na realidade, isso não é bem verdade , de maneiras desagradáveis.
O caso mais comum são eventos imediatos. Os navegadores os acionam imediatamente quando seu código faz algo para causar-lhes:
Resultados em
log in, blur, log out
todos, exceto no IE. Esses eventos não são acionados apenas porque você ligoufocus()
diretamente, eles podem acontecer porque você ligoualert()
ou abriu uma janela pop-up ou qualquer outra coisa que mova o foco.Isso também pode resultar em outros eventos. Por exemplo, adicione um
i.onchange
ouvinte e digite algo na entrada antes que afocus()
chamada o desvie, e a ordem do log serálog in, change, blur, log out
, exceto no Opera, onde estálog in, blur, log out, change
e no IE, onde está (ainda menos explicitamente)log in, change, log out, blur
.Da mesma forma, chamar
click()
um elemento que o fornece chama oonclick
manipulador imediatamente em todos os navegadores (pelo menos isso é consistente!).(Estou usando as
on...
propriedades do manipulador de eventos diretos aqui, mas o mesmo acontece comaddEventListener
eattachEvent
.)Também existem várias circunstâncias em que os eventos podem ser disparados enquanto o código é encadeado, apesar de você não ter feito nada para provocá-lo. Um exemplo:
Pressione
alert
e você receberá uma caixa de diálogo modal. Não é mais necessário executar o script até você descartar esse diálogo, sim? Não. Redimensione a janela principal e você entraráalert in, resize, alert out
na área de texto.Você pode pensar que é impossível redimensionar uma janela enquanto uma caixa de diálogo modal estiver aberta, mas não é assim: no Linux, você pode redimensionar a janela o quanto quiser; no Windows, não é tão fácil, mas você pode fazer isso alterando a resolução da tela de maior para menor, onde a janela não se encaixa, fazendo com que ela seja redimensionada.
Você pode pensar, bem, é apenas
resize
(e provavelmente mais um poucoscroll
) que pode ser acionado quando o usuário não tem interação ativa com o navegador porque o script é encadeado. E para janelas únicas, você pode estar certo. Mas tudo isso vai para o pote assim que você cria scripts entre janelas. Para todos os navegadores que não sejam o Safari, que bloqueiam todas as janelas / guias / quadros quando qualquer um deles está ocupado, você pode interagir com um documento a partir do código de outro documento, executando em um encadeamento de execução separado e fazendo com que qualquer manipulador de eventos relacionado fogo.Locais onde os eventos que você pode causar a geração podem ser gerados enquanto o script ainda está em thread:
quando os popups modais (
alert
,confirm
,prompt
) estão abertas, em todos os navegadores, mas Opera;durante
showModalDialog
nos navegadores que o suportam;a caixa de diálogo "Um script nesta página pode estar ocupado ...", mesmo se você optar por deixar o script continuar em execução, permite que eventos como redimensionar e desfocar sejam acionados e sejam tratados mesmo quando o script estiver no meio de um ocupado, exceto no Opera.
Há algum tempo, para mim, no IE com o Sun Java Plugin, chamar qualquer método em um applet poderia permitir que os eventos disparassem e o script fosse reinserido. Esse sempre foi um bug sensível ao tempo, e é possível que a Sun o tenha corrigido desde então (espero que sim).
provavelmente mais. Já faz um tempo desde que eu testei isso e os navegadores ganharam complexidade desde então.
Em resumo, o JavaScript parece para a maioria dos usuários, na maioria das vezes, ter um único encadeamento de execução estrito e controlado por eventos. Na realidade, não existe. Não está claro quanto disso é simplesmente um bug e qual é o design deliberado, mas se você estiver escrevendo aplicativos complexos, especialmente os de janela cruzada / scripts de quadros, há todas as chances de que isso o atinja - e de forma intermitente, maneiras difíceis de depurar.
Se o pior acontecer, você pode resolver problemas de simultaneidade indiretamente todas as respostas do evento. Quando um evento chegar, solte-o em uma fila e lide com a fila em ordem posterior, em uma
setInterval
função. Se você estiver escrevendo uma estrutura que pretende ser usada por aplicativos complexos, isso pode ser uma boa jogada.postMessage
esperançosamente também aliviará a dor da criação de scripts entre documentos no futuro.fonte
blur
após o código retornar o controle para o navegador.Eu diria que sim - porque praticamente todo o código javascript existente (pelo menos não trivial) seria interrompido se o mecanismo javascript do navegador fosse executado de forma assíncrona.
Acrescente a isso o fato de que o HTML5 já especifica Trabalhadores da Web (uma API padronizada e explícita para código javascript com vários threads) que introduza vários threads no Javascript básico não faria sentido.
( Observação para outros comentaristas: mesmo assim
setTimeout/setInterval
, os eventos de solicitação de carga HTTP (XHR) e os eventos da interface do usuário (clique, foco, etc.) fornecem uma impressão grosseira de multithreaded - eles ainda são todos executados em uma única linha do tempo - uma em por um tempo - portanto, mesmo que não conheçamos sua ordem de execução de antemão, não há necessidade de se preocupar com alterações nas condições externas durante a execução de um manipulador de eventos, função temporizada ou retorno de chamada XHR.)fonte
Sim, embora você ainda possa sofrer alguns problemas de programação simultânea (principalmente condições de corrida) ao usar qualquer uma das APIs assíncronas, como retornos de chamada setInterval e xmlhttp.
fonte
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.
fonte
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 sincronização para acessar o estado do objeto compartilhado.
Eu acho que o paradigma não-bloqueador de thread único surgiu da necessidade de executar o javascript nos navegadores onde a interface do usuário nunca deve bloquear.
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 contexto, mas podem compartilhar escopo. Para este caso específico, a documentação declara:
Ao ler a documentação do Rhino, concluo que é possível alguém escrever uma API javascript que também gera novos threads javascript, mas a API seria específica para o rinoceronte (por exemplo, o nó 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 consideram multiencadeamento ou bloqueio.
Concearning navegadores e nodejs da maneira que eu vejo:
Portanto, no caso de navegadores e nodejs (e provavelmente muitos outros mecanismos), o javascript não é multithread, mas os próprios mecanismos .
Atualização sobre os trabalhadores da Web:
A presença de trabalhadores da web justifica ainda mais que o javascript pode ser multiencadeado, no sentido de que alguém pode criar código em javascript que será executado em um encadeamento separado.
No entanto: os trabalhadores da Web não corrigem os problemas dos threads tradicionais que podem compartilhar o contexto de execução. As regras 2 e 3 acima ainda se aplicam , mas desta vez o código encadeado é criado pelo usuário (gravador de código js) em javascript.
A única coisa a considerar é o número de threads gerados, do ponto de vista da eficiência (e não da simultaneidade ). Ver abaixo:
Sobre a segurança da linha :
PS
Além da teoria, esteja sempre preparado sobre possíveis casos de canto e bugs descritos na resposta aceita
fonte
JavaScript / ECMAScript foi projetado para viver em um ambiente host. Ou seja, o JavaScript não faz nada , a menos que o ambiente host decida analisar e executar um determinado script e forneça objetos de ambiente que permitam que o JavaScript seja realmente útil (como o DOM nos navegadores).
Eu acho que uma determinada função ou bloco de script executará linha por linha e isso é garantido para JavaScript. No entanto, talvez um ambiente host possa executar vários scripts ao mesmo tempo. Ou, um ambiente host sempre pode fornecer um objeto que oferece multiencadeamento.
setTimeout
esetInterval
são exemplos, ou pelo menos pseudo-exemplos, de um ambiente host que fornece uma maneira de fazer alguma simultaneidade (mesmo que não seja exatamente simultânea).fonte
Na verdade, uma janela pai pode se comunicar com janelas ou quadros filhos ou irmãos que possuem seus próprios threads de execução em execução.
fonte
O @Bobince está fornecendo uma resposta realmente opaca.
Partindo da resposta de Már Örlygsson, o Javascript é sempre de thread único por causa deste simples fato: tudo em Javascript é executado em uma única linha do tempo.
Essa é a definição estrita de uma linguagem de programação de thread único.
fonte
Não.
Estou indo contra a multidão aqui, mas tenha paciência comigo. Um único script JS deve ser efetivamente único encadeado, mas isso não significa que não possa ser interpretado de maneira diferente.
Digamos que você tenha o seguinte código ...
Isso está escrito com a expectativa de que, no final do loop, a lista deva ter 10000 entradas que são o índice ao quadrado, mas a VM poderá perceber que cada iteração do loop não afeta a outra e reinterpretar usando dois threads.
Primeira discussão
Segunda discussão
Estou simplificando aqui, porque matrizes JS são mais complicadas do que pedaços de memória idiotas, mas se esses dois scripts puderem adicionar entradas à matriz de uma maneira segura para threads, quando a execução de ambos terminar, haverá o mesmo resultado que a versão single-threaded.
Embora eu não esteja ciente de qualquer VM detectando código paralelizável como este, parece provável que ele possa existir no futuro para VMs JIT, pois pode oferecer mais velocidade em algumas situações.
Levando esse conceito adiante, é possível que o código possa ser anotado para que a VM saiba o que converter em código multiencadeado.
Como os Web Workers estão adotando o Javascript, é improvável que esse sistema mais feio venha a existir, mas acho seguro dizer que o Javascript é único por tradição.
fonte
Bem, o Chrome é multiprocesso, e acho que todo processo lida com seu próprio código Javascript, mas, tanto quanto o código sabe, é "single-threaded".
Não há suporte algum em Javascript para multiencadeamento, pelo menos não explicitamente, portanto isso não faz diferença.
fonte
Eu tentei o exemplo do @ bobince com algumas modificações:
Portanto, ao pressionar Executar, feche o pop-up de alerta e faça um "thread único", você verá algo assim:
Mas se você tentar executar isso no Opera ou Firefox estável no Windows e minimizar / maximizar a janela com alerta pop-up na tela, haverá algo como isto:
Não quero dizer que isso é "multithreading", mas algum código foi executado em um momento errado comigo, sem esperar por isso, e agora estou com um estado corrompido. E melhor saber sobre esse comportamento.
fonte
Tente aninhar duas funções setTimeout uma na outra e elas se comportarão com vários segmentos (ou seja, o timer externo não esperará que o interno seja concluído antes de executar sua função).
fonte
setTimeout(function(){setTimeout(function(){console.log('i herd you liek async')}, 0); alert('yo dawg!')}, 0)
(para o registro, yo dawg deve vir sempre em primeiro lugar, então a saída do log console)