Movatterモバイル変換


[0]ホーム

URL:


MDN Web Docs

Esta página foi traduzida do inglês pela comunidade. Saiba mais e junte-se à comunidade MDN Web Docs.

Modelo de Concorrência e Event Loop

O JavaScript possui um modelo de concorrência baseado em umevent loop (laço de eventos), em português), responsável pela execução do código, coleta e processamento de eventos e execução de subtarefas enfileiradas. Este modelo é bem diferente de outras linguagens, como C ou Java, por exemplo.

Conceitos de runtime (tempo de execução)

Os próximos tópicos irão explicar teoricamente o modelo. Interpretadores modernos de JavaScript implementam e otimizam fortemente as semânticas descritas.

Representação visual

Stack, heap, queue

Pilha (Stack)

As chamadas de funções criam uma pilha deframes (quadros).

js
function foo(b) {  let a = 10;  return a + b + 11;}function bar(x) {  let y = 3;  return foo(x * y);}const baz = bar(7); // atribui 42 a baz

Ordem das operações:

  1. Quando chamamos a funçãobar, o primeiroframe é criado contendo argumentos e variáveis locais debar.
  2. Quando a funçãobar chamafoo, o segundoframe é criado e é colocado no topo da pilha, contendo os argumentos e as variáveis locais defoo.
  3. Quandofoo retorna, oframe do topo é removido da pilha (deixando apenas oframe da chamada debar).
  4. Quandobar retorna, a pilha fica vazia.

Note que os argumentos e variáveis locais podem continuar existindo, pois são armazenados fora da pilha — de forma que podem ser acessados por quaisquerfunções aninhadas muito depois de sua função externa ter retornado.

Acervo (Heap)

Os objetos são alocados em umheap (acervo), que é apenas um nome para denotar uma grande região não estruturada da memória.

Fila (Queue)

Oruntime do JavaScript contém uma fila de mensagens, que é uma lista de mensagens a serem processadas. Cada mensagem é associada a uma função, que é chamada para lidar com a mensagem.

Em algum ponto durante oevent loop (laço de eventos), oruntime começa a manipular as mensagens na fila, começando pela mais antiga. Para fazer isso, a mensagem é removida da fila e sua função correspondente é chamada com a mensagem como um parâmetro deinput (entrada). Como de costume, chamar uma função cria um novoframe (quadro) na pilha para uso dessa função.

O processamento de funções continua até que a pilha fique novamente vazia, então oevent loop processará a próxima mensagem na fila (se houver uma).

Laço de Eventos (Event loop)

OEvent loop tem esse nome por causa da forma que normalmente é implementado, que se assemelha a:

js
while (queue.waitForMessage()) {  queue.processNextMessage();}

queue.waitForMessage aguarda, de maneira síncrona, receber uma mensagem (se não houver nenhuma já disponível esperando para ser tratada).

Processamento Completo ("Run-to-completion")

Cada mensagem é processada completamente antes de outra mensagem ser processada. Isso oferece um bom fundamento ao pensar sobre o seusoftware, incluindo o fato de que, independente de quando uma função é executada, ela não pode ser interrompida e, portanto, será executada por completo antes que outro código rode (e modifique dados manipuláveis pela função). Isso é diferente do C, por exemplo, no qual uma função que está sendo executada em umathread (um fluxo de execução), pode ser interrompida a qualquer momento para executar um outro código em outrathread.

Um aspecto negativo deste modelo é que se uma mensagem levar muito tempo para ser finalizada, a aplicação web ficará indisponível para processar as interações do usuário, como cliques ou rolagens. O navegador mitiga este problema através do aviso: "Um script desta página pode estar ocupado, ou parou de responder". Uma boa prática a seguir é fazer o processamento de mensagens curtas, e, se possível, dividir uma longa mensagem em múltiplas mensagens menores.

Adicionando mensagens

Nos navegadores, as mensagens são adicionadas a qualquer momento que um evento é acionado se este possuir umlistener (ouvinte). Caso não haja, o evento será ignorado. Assim, um clique em um elemento com um manipulador de eventos de clique adicionará uma mensagem, como qualquer outro evento.

A funçãosetTimeout é chamada com 2 argumentos: uma mensagem para adicionar à fila (queue) e um valor em tempo (opcional, o padrão é0). O valor de tempo (time value) representa o intervalo (mínimo) com que a mensagem será realmente enviada à fila. Se não houver outra mensagem na fila, a mensagem será processada logo após o intervalo. No entanto, caso haja mensagens, a mensagemsetTimeout terá que esperar até que outras mensagens sejam processadas. Por esse motivo, o segundo argumento indica um tempomínimo e não um tempogarantido.

Aqui está um exemplo que demonstra esse conceito (setTimeout não é executado imediatamente após o temporizador expirar):

js
const s = new Date().getSeconds();setTimeout(function () {  // imprime "2", o que significa que o callback não é chamado imediatamente após 500 milissegundos.  console.log("Ran after " + (new Date().getSeconds() - s) + " seconds");}, 500);while (true) {  if (new Date().getSeconds() - s >= 2) {    console.log("Good, looped for 2 seconds");    break;  }}

Intervalos de zero segundos

O intervalo zero não significa, necessariamente, que ocallback será disparado após zero milissegundos. ChamarsetTimeout com um intervalo de0 (zero) milissegundos não executa a função docallback após intervalo dado.

A execução depende do número de mensagens em espera na fila. No exemplo abaixo, a mensagem''this is just a message'' será escrita no console antes que a mensagem docallback seja processada, porque, como informado previamente, o intervalo definido na função indica o tempomínimo necessário para que a aplicação processe a requisição e não o tempoexato de processamento.

Basicamente,setTimeout precisa esperar que todo o código das mensagens enfileiradas seja concluído, mesmo que você tenha especificado um tempo limite específico para o seusetTimeout.

js
(function () {  console.log("this is the start");  setTimeout(function cb() {    console.log("Callback 1: this is a msg from call back");  }); // tem um valor de tempo padrão de 0  console.log("this is just a message");  setTimeout(function cb1() {    console.log("Callback 2: this is a msg from call back");  }, 0);  console.log("this is the end");})();// "this is the start"// "this is just a message"// "this is the end"// "Callback 1: this is a msg from call back"// "Callback 2: this is a msg from call back"

Múltiplosruntimes comunicando-se em conjunto

Umweb worker ou umiframe com uma diferente origem(cross-origin) tem seu própriostack,heap equeue de messagens. Doisruntimes distintos só podem se comunicar por meio do envio de mensagens via métodopostMessage. Este método adiciona uma mensagem ao outroruntime, se este escutar os eventos demessage.

Sem bloqueio

Uma propriedade muito interessante do modeloevent loop é que o JavaScript, ao contrário de muitas outras linguagens, nunca bloqueia. A manipulação de E/S (I/O) é tipicamente realizada através de eventos ecallbacks. Portanto, quando uma aplicação está esperando pelo retorno de uma consulta doIndexedDB ou de uma requisiçãoXHR, ela ainda pode processar outras coisas, como as ações do usuário.

Exceções de legado existem, como por exemplo,alert ou XHR síncrono, mas é considerado uma boa prática evitá-los. Tome cuidado,exceções a exceção existem (mas geralmente são, mais do que qualquer coisa,bugs de implementação).

Veja também

Help improve MDN

Learn how to contribute.

This page was last modified on byMDN contributors.


[8]ページ先頭

©2009-2025 Movatter.jp