Event Loop e Orientação a Eventos em JavaScript

Aplicações modernas precisam responder continuamente a acontecimentos que não ocorrem em uma sequência totalmente previsível. Um usuário pode clicar em um botão, uma requisição HTTP pode terminar, um temporizador pode expirar ou novos dados podem chegar pela rede. Esse tipo de comportamento está relacionado à chamada orientação a eventos, na qual determinadas partes do sistema são executadas em resposta à ocorrência de eventos.

Na Engenharia de Software, eventos são importantes para descrever o comportamento dinâmico dos sistemas. Sommerville (2011) afirma que uma das perspectivas utilizadas na modelagem comportamental consiste em representar “como ele reage aos eventos”. Portanto, em vez de compreender um programa apenas como uma sequência fixa de instruções, pode-se observá-lo como um sistema que permanece disponível para reagir a acontecimentos internos ou externos.

Essa abordagem é muito relevante para sistemas construídos em JavaScript. Navegadores precisam executar scripts ao mesmo tempo em que tratam interações do usuário, comunicação de rede, temporizadores e atualização da interface gráfica. O mecanismo responsável por coordenar grande parte dessas atividades é denominado event loop, ou laço de eventos. O padrão HTML determina que agentes de execução utilizem event loops justamente para coordenar eventos, scripts, interação do usuário, renderização e comunicação de rede (WHATWG, 2026).

O que é orientação a eventos?

A orientação a eventos, ou event-driven programming, é um modelo de construção de software no qual parte do fluxo de execução é determinada pela ocorrência de eventos. Em vez de o programa controlar antecipadamente toda a ordem das operações, determinadas funções são registradas para serem executadas quando algum acontecimento específico ocorrer.

Um evento pode representar diversos tipos de acontecimentos, como:

  • um clique do usuário;
  • o pressionamento de uma tecla;
  • a chegada de uma mensagem;
  • a conclusão de uma requisição;
  • o término de um temporizador;
  • uma alteração de estado;
  • a disponibilidade de novos dados.

Em sistemas orientados a eventos, portanto, existe uma relação conceitual entre evento e tratador do evento (event handler). Quando determinado evento ocorre, o ambiente identifica a função associada àquele acontecimento e providencia sua execução.

Essa forma de organização aparece em diferentes tipos de software, sobretudo interfaces gráficas, servidores, sistemas distribuídos e aplicações web. Considere, por exemplo, o código abaixo:

const botao = document.querySelector("#salvar");

botao.addEventListener("click", () => {
    console.log("Dados salvos.");
});

O código não chama explicitamente a função registrada em addEventListener(). Ele apenas informa ao navegador:

quando ocorrer um clique nesse elemento, execute esta função.

A execução futura será determinada pelo evento.

Inversão de controle

A orientação a eventos está fortemente relacionada ao conceito de inversão de controle (do inglês, Inversion of ControlIoC).

Em um programa estritamente sequencial, o próprio código normalmente determina quando uma função será chamada:

function salvarDados() {
    console.log("Dados salvos.");
}

salvarDados();

Na programação orientada a eventos, porém, o desenvolvedor registra uma função e entrega ao ambiente a responsabilidade de decidir quando ela será chamada:

botao.addEventListener("click", salvarDados);

Nesse caso, o desenvolvedor criou a função, mas quem efetivamente controla sua chamada é o ambiente de execução.

Sommerville (2011) apresenta essa ideia ao discutir que loops de eventos pertencentes à plataforma ou à interface gráfica acionam callbacks fornecidos pela aplicação. Esse conceito é importante para compreendermos o funcionamento do JavaScript, pois grande parte da programação assíncrona consiste em fornecer funções que serão chamadas posteriormente pelo ambiente.

O que é o Event Loop?

O event loop é um mecanismo de coordenação responsável por determinar quando trabalhos pendentes podem ser executados. Segundo o padrão HTML, o event loop coordena atividades como eventos, interação do usuário, execução de scripts, renderização e operações de rede (WHATWG, 2026).

A finalidade central do event loop é permitir que um ambiente reativo execute diferentes tipos de trabalho sem precisar bloquear sua execução aguardando cada acontecimento externo.

Em aplicações Web isso possibilita coordenar cliques, requisições HTTP, temporizadores, Promises, eventos de teclado, mensagens, renderização e callbacks sem exigir que o programa permaneça parado esperando individualmente cada operação.

O modelo é especialmente adequado para aplicações que realizam grande quantidade de operações de entrada e saída, nas quais boa parte do tempo seria desperdiçada aguardando dispositivos, redes ou outros sistemas.

Contudo, o event loop não torna automaticamente eficiente qualquer programa. Operações computacionalmente caras continuam consumindo tempo de processamento e podem bloquear outros trabalhos dependentes daquele mesmo agente.

Uma interpretação didática seria imaginar o event loop perguntando continuamente:

Existe algum trabalho que já pode ser executado?

Quando existe, um trabalho adequado é selecionado para execução. Depois que esse trabalho termina, o mecanismo continua verificando as atividades pendentes.

Operações assíncronas

Considere uma requisição de rede como a descrita abaixo:

fetch("/api/produtos")
    .then(resposta => resposta.json())
    .then(produtos => console.log(produtos));

console.log("Programa continua");

Nes cenário, seria inadequado bloquear toda a aplicação enquanto o servidor remoto responde. Então, o ambiente inicia a operação de rede e permite que o programa continue executando. Quando a operação alcança determinado estado, trabalhos relacionados à continuação daquela operação podem ser agendados.

Assim, o fato do sistema funcionar assíncrono não significa necessariamente executar código JavaScript simultaneamente. Significa que uma operação pode ser iniciada e seu resultado tratado posteriormente, sem exigir que o fluxo principal permaneça bloqueado aguardando sua conclusão.

Pilha de chamadas

Para gerenciar as chamadas das funções, os dados são colocados em espaços reservados de memória associados à aplicação. Esse espaço é chamado de call stack, ou pilha de chamadas.

Sempre que uma função é executada, cria-se um contexto de execução que é colocado na pilha. Se essa função chamar outra função, um novo contexto será colocado sobre o anterior.

Considere o código a seguir:

function calcularTotal() {
    return calcularSubtotal() + 10;
}

function calcularSubtotal() {
    return 90;
}

console.log(calcularTotal());

Uma representação simplificada das chamadas acima seria:

console.log()
    │
calcularTotal()
    │
calcularSubtotal()

Quando calcularSubtotal() termina, seu contexto é removido da call stack. Depois calcularTotal() termina e também é removido do espaço reservado. Esse comportamento segue o princípio LIFO — Last In, First Out.

Entretanto, o funcionamento real é mais sofisticado do que simplesmente retirar funções de uma única pilha síncrona. Por padrão, o conteúdo da call stack é executado primeiro. Porém, o padrão HTML também define diferentes task queues (filas de tarefas), além de uma fila específica de microtasks (WHATWG, 2026).

Tasks

O padrão HTML estabelece que um event loop possui uma ou mais task queues. Tasks representam trabalhos associados, por exemplo, a eventos, callbacks, processamento de documentos e utilização de recursos (WHATWG, 2026).

Eventos de interação podem produzir tasks, como o exemplo abaixo:

botao.addEventListener("click", () => {
    console.log("Clique processado");
});

Temporizadores também podem levar ao agendamento posterior de trabalho:

setTimeout(() => {
    console.log("Temporizador concluído");
}, 1000);

Neste caso, é importante observar que setTimeout() não significa: “execute exatamente daqui a 1000 ms”. A interpretação mais apropriada é aproximadamente pensar assim:

depois que o tempo mínimo estabelecido for alcançado, torne o callback elegível para execução conforme as regras de agendamento.

Caso o JavaScript esteja ocupado, o callback precisará aguardar o tempo necessário para sua execução. Esse tempo é variável e depende da fila de tarefas, além da fila de microtasks.

Microtasks

Além das tasks, existe uma estrutura particularmente importante: a microtask queue. Os objetos Promises utilizam esse mecanismo de agendamento para lidar com as diversas tarefas.

A especificação ECMAScript estabelece que reações de Promises são agendadas como Jobs para execução posterior (ECMA International, 2025). No ambiente web, os Jobs relacionados às Promises são integrados ao mecanismo de microtasks.

Tasks × microtasks

Uma das regras mais importantes para compreender a ordem de execução em JavaScript é que microtasks pendentes são processadas antes que o event loop avance normalmente para uma próxima task. De maneira simplificada, podemos dizer que o fluxo seria:

Executa código atual
     ↓
Pilha (call stack) fica vazia 
     ↓
Processa microtasks pendentes (microtasks queue)
     ↓
Event loop continua
     ↓
Próxima task (tasks queue)

O MDN descreve que as microtasks recebem tratamento prioritário no modelo utilizado pelo ambiente HTML, sendo a fila de microtasks processada antes da obtenção de uma nova task (MDN Web Docs, 2026).

Essa diferença explica a execução do exemplo seguinte.

console.log("1 - início");

setTimeout(() => {
    console.log("4 - temporizador");
}, 0);

Promise.resolve().then(() => {
    console.log("3 - Promise");
});

console.log("2 - fim");

A saída será:

1 - início
2 - fim
3 - Promise
4 - temporizador

O código síncrono executa primeiro:

1 - início
2 - fim

A função associada à Promise é executada posteriormente como microtask:

3 - Promise

Somente depois o trabalho (Job) associado ao temporizador poderá ser processado (como uma task):

4 - temporizador

Tudo isso ocorre “por debaixo do capô” de funcionamento dos ambientes de execução de linguagens que tenham suporte a orientação a eventos, como o JavaScript.

Funcionamento das prioridades

As Figuras de 1 a 6 apresentam a dinâmica do fluxo de funcionamento do event loop para lidar com a call stack, as tasks queues e as microtasks queues.






Conclusão

Orientação a eventos é um paradigma particularmente apropriado para sistemas cujo comportamento depende de acontecimentos imprevisíveis ou externos. Em vez de determinar toda a sequência de execução antecipadamente, a aplicação registra comportamentos capazes de responder quando determinados eventos ocorrerem.

No ecossistema JavaScript, o event loop fornece a infraestrutura necessária para coordenar grande parte desse modelo. Em ambientes web, ele articula execução de scripts, eventos, tarefas, microtasks, operações de rede e renderização (WHATWG, 2026).

Obrigado pela leitura e bons estudos!

Referências

ECMA INTERNATIONAL. ECMAScript 2025 Language Specification. Geneva: Ecma International, 2025. Disponível em: https://tc39.es/ecma262/2025/. Acesso em: 16 set. 2026.

MDN WEB DOCS. JavaScript execution model. [S. l.]: Mozilla, 2026. Disponível em: <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Execution_model>. Acesso em: 16 set. 2026.

SOMMERVILLE, Ian. Engenharia de software. 9. ed. São Paulo: Pearson Prentice Hall, 2011.

WHATWG. HTML Standard: Web application APIs — Event loops. [S. l.]: WHATWG, 2026. Disponível em: <https://html.spec.whatwg.org/multipage/webappapis.html>. Acesso em: 16 set. 2026.