16 métodos para adiar ou agendar JavaScript

Aprenda como adiar e agendar JavaScript

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-02-24

Por que adiar ou agendar JavaScript?

O JavaScript pode (e irá) deixar o seu site mais lento de algumas maneiras. Isso pode ter todo tipo de impacto negativo nos Core Web Vitals. O JavaScript pode competir por recursos de rede, pode competir por recursos de CPU (bloquear a main thread) e pode até bloquear o parsing de uma página da web. Adiar e agendar os seus scripts pode melhorar drasticamente os Core Web Vitals.

Revisado pela última vez por Arjen Karel em fevereiro de 2026

Para minimizar os efeitos desagradáveis que o JavaScript pode ter nos Core Web Vitals, geralmente é uma boa ideia especificar quando um script entra na fila para download e agendar quando ele pode ocupar tempo de CPU e bloquear a main thread.

Como o tempo do JavaScript pode afetar os Core Web Vitals?

Como o tempo do JavaScript pode afetar os Core Web Vitals? Basta dar uma olhada neste exemplo da vida real. A primeira página é carregada com JavaScript 'render blocking'.  As métricas de paint, assim como o Total Blocking Time, são muito ruins. O segundo exemplo é exatamente da mesma página, mas com o JavaScript deferred. Você verá que a imagem do LCP ainda sofreu um grande impacto. O terceiro exemplo tem o mesmo script executado após o 'load event' da página e tem as chamadas de função divididas em pedaços menores. Este último passa nos Core Web Vitals com folga.

null
null
null


Por padrão, o JavaScript externo no head da página bloqueará a criação da render tree. Mais especificamente: quando o navegador encontra um script no documento, ele deve pausar a construção do DOM, entregar o controle ao runtime do JavaScript e deixar o script executar antes de prosseguir com a construção do DOM. Isso afetará suas métricas de Paint (Largest Contentful Paint e First Contentful Paint).

O JavaScript deferred ou async ainda pode impactar as métricas de paint, especialmente o Largest Contentful Paint, porque ele será executado e bloqueará a main thread, assim que o DOM tiver sido criado (e elementos comuns do LCP, como imagens, podem não ter sido baixados).

Arquivos JavaScript externos também irão competir por recursos de rede. Arquivos JavaScript externos geralmente são baixados antes das imagens. Se você estiver baixando muitos scripts, o download das suas imagens será atrasado.

Por último, mas não menos importante, o JavaScript pode bloquear ou atrasar a interação do usuário. Quando um script está usando recursos de CPU (bloqueando a main thread), um navegador não responderá à entrada (cliques, rolagem, etc.) até que esse script seja concluído. Isso afeta diretamente a sua pontuação de Interaction to Next Paint (INP).

O impacto é mensurável. De acordo com o Web Almanac 2025, apenas 15% das páginas mobile passam na auditoria de recursos render-blocking. O Total Blocking Time mediano em mobile é de 1.916 milissegundos. São quase 2 segundos inteiros em que o navegador não consegue responder à entrada do usuário. Escolher o método defer certo para cada script é como você reduz esse número.

Como agendar ou adiar o JavaScript conserta os Core Web Vitals?

Agendar ou adiar o JavaScript não conserta os Core Web Vitals por si só. É tudo uma questão de usar a ferramenta certa para a situação certa. Como regra, você deve tentar atrasar seus scripts o mínimo possível, mas colocá-los na fila de download e executá-los no momento apropriado.

Como escolher o método defer correto?

Nem todos os scripts são iguais e cada script tem a sua própria funcionalidade. Alguns scripts são importantes de se ter no início do processo de renderização, outros não.

null

Eu gosto de categorizar os JavaScripts em 4 grupos com base no seu nível de importância.

1. Render critical. Estes são os scripts que mudarão a aparência de uma página da web. Se eles não carregarem, a página não parecerá completa. Estes scripts devem ser evitados a todo custo. Se você não puder evitá-los por algum motivo, eles não devem ser deferred. Por exemplo, um slider superior ou um script de teste A/B.
2. Critical. Estes scripts não mudarão a aparência de uma página da web (muito), mas a página não funcionará bem sem eles. Estes scripts devem ser deferred ou async. Por exemplo, os seus scripts de menu.
3. Important. Estes são scripts que você deseja carregar porque eles são valiosos para você ou para o visitante. Eu tendo a carregar esses scripts após o 'load event' ter sido disparado. Por exemplo, analytics ou um botão de 'voltar ao topo'.
4. Nice to have. Estes são scripts sem os quais você pode viver se absolutamente precisar. Eu carrego esses scripts com a menor das prioridades e só os executo quando o navegador está ocioso. Por exemplo, um widget de chat ou um botão do facebook.

Método 1: Usar o atributo defer

Scripts com o atributo defer serão baixados em paralelo e são adicionados à fila de JavaScript defer. Pouco antes do navegador disparar o evento DOMContentLoaded, todos os scripts nessa fila serão executados na ordem em que aparecem no documento.

<script defer src='javascript.js'></script>

O 'truque do defer' geralmente resolve muitos problemas, especialmente as métricas de paint. Infelizmente não há garantia, depende da qualidade dos scripts. Scripts deferred serão executados assim que todos os scripts tiverem sido carregados e o HTML for analisado (DOMContentLoaded). O elemento LCP (geralmente uma imagem grande) pode não estar carregado até lá e os scripts deferred ainda causarão um atraso no LCP.

Quando usar:

Use scripts deferred para scripts Critical que são necessários o mais rápido possível.

Vantagens:

  1. Scripts deferred serão baixados em paralelo
  2. O DOM estará disponível no momento da execução

Desvantagens:

  1. Scripts deferred podem atrasar suas métricas de LCP 
  2. Scripts deferred bloquearão a main thread assim que forem executados
  3. Pode não ser seguro fazer o defer de scripts quando scripts inline ou async dependem deles

Método 2: Usar o atributo async

Scripts com o atributo async baixam em paralelo e serão executados imediatamente após terminarem o download.

<script async src='javascript.js'></script>

Scripts async farão pouco para corrigir seus problemas de pagespeed. É ótimo que eles sejam baixados em paralelo, mas é só isso. Uma vez baixados, eles bloquearão a main thread à medida que são executados.

Quando usar:

Use scripts async para scripts Critical que são necessários o mais rápido possível e são independentes (não dependem de outros scripts).

Vantagens:

  1. Scripts async serão baixados em paralelo.
  2. Scripts async serão executados o mais rápido possível.

Desvantagens:

  1. O DOMContentLoaded pode acontecer tanto antes quanto depois do async.
  2. A ordem de execução dos scripts será desconhecida de antemão.
  3. Você não pode usar scripts async que dependem de outros scripts async ou deferred

Para uma comparação detalhada dessas duas abordagens, veja defer vs async e como isso afeta os Core Web Vitals.

Método 3: Usar modules

Scripts modulares são deferred por padrão, a menos que tenham o atributo async. Nesse caso, eles serão tratados como scripts async

<script module src='javascript.js'></script>

Modules são uma nova maneira de pensar sobre JavaScript e corrigem algumas falhas de design. Além disso, usar script modules não acelerará o seu site. 

Quando usar:

Quando a sua aplicação é construída de forma modular, faz sentido usar também os JavaScript modules.

Vantagens:

  1. Modules são deferred por padrão
  2. Modules são mais fáceis de manter e funcionam muito bem com design web modular
  3. Modules permitem a divisão fácil de código com importações dinâmicas (dynamic imports), onde você importa apenas os módulos que precisa em um determinado momento.

Desvantagens:

  1. Modules por si só não melhorarão os Core Web Vitals
  2. Importar modules just-in-time ou em tempo real pode ser lento e piorar o INP 

Método 4: Colocar os scripts perto do final da página

Scripts de rodapé são colocados na fila para download em um momento posterior. Isso priorizará outros recursos que estão no documento acima da tag do script.

<html>
   <head></head>
   <body>
      [o conteúdo da sua página aqui]
      <script defer src='javascript.js'></script>
   </body>
</html>

Colocar seus scripts no final da página é uma técnica interessante. Isso agendará outros recursos (como imagens) antes de seus scripts. Isso aumentará a chance de que eles estejam disponíveis para o navegador e pintados na tela antes que os arquivos JavaScript terminem o download e a main thread seja bloqueada pela execução do script. Ainda assim... sem garantias.

Quando usar:

Quando os seus scripts já estão com um desempenho muito bom, mas você deseja priorizar levemente outros recursos como imagens e webfonts.

Vantagens:

  1. Colocar scripts no final da página não requer muito conhecimento.
  2. Se feito corretamente, não há risco de quebrar a sua página

Desvantagens:

  1. Scripts critical podem ser baixados e executados mais tarde
  2. Não corrige nenhum problema subjacente de JavaScript

Método 5: Injetar scripts

Scripts injetados são tratados como scripts async. Eles são baixados em paralelo e executados imediatamente após o download.

<script>
    const loadScript = (scriptSource) => {
        const script = document.createElement('script');
        script.src = scriptSource;
        document.head.appendChild(script);
    }

    // chama a função loadscript que injeta 'javascript.js'
    loadScript('javascript.js');
</script>

Da perspectiva dos Core Web Vitals, essa técnica é exatamente a mesma que usar <script async>.

Quando usar:

Este método é frequentemente usado por scripts de terceiros que são acionados o mais cedo possível. A chamada da função facilita o encapsulamento e a compressão do código.

Vantagens:

  1. Código contido que injeta um script async.

Desvantagens:

  1. O DOMContentLoaded pode acontecer tanto antes quanto depois do script ter carregado.
  2. A ordem de execução dos scripts será desconhecida de antemão.
  3. Você não pode usar isso em scripts que dependem de outros scripts async ou deferred

Método 6: Injetar scripts em um momento posterior

Scripts nice-to-have, na minha opinião, nunca deveriam ser carregados deferred. Eles devem ser injetados no momento mais oportuno. No exemplo abaixo, o script será executado após o navegador ter enviado o evento 'load'.

<script>
window.addEventListener('load', function () {
  // veja o método 5 para a função loadscript
  loadScript('javascript.js');
});
</script>

Esta é a primeira técnica que irá melhorar de forma confiável o Largest Contentful Paint. Todos os recursos importantes, incluindo imagens, serão baixados quando o navegador disparar o 'load event'. Isso pode introduzir todo tipo de problema porque pode levar muito tempo para o load event ser chamado.

Quando usar:

Para scripts nice-to-have que não têm razão para impactar as métricas de paint.

Vantagens:

  1. Não competirá por recursos críticos porque injetará o script assim que a página e seus recursos forem carregados

Desvantagens:

  1. Se a sua página for mal projetada em termos de Core Web Vitals, pode levar muito tempo para a página enviar o evento 'load'
  2. Você precisa ter cuidado para não aplicar isso a scripts critical (como lazy loading, menu, etc)

Método 7: Alterar o tipo de script (e depois alterá-lo de volta)

Se uma tag script for encontrada em algum lugar na página que 1. tenha um atributo type e 2. o atributo type não seja "text/javascript", o script não será baixado e executado pelo navegador. Muitos JavaScript Loaders (como o RocketLoader da CloudFlare) dependem desse princípio. A ideia é bem simples e elegante.

Primeiro, todos os scripts são reescritos assim:

<script type="some-cool-script-type" src="javascript.js"></script>

Então, em algum ponto durante o processo de carregamento, esses scripts são convertidos de volta para 'javascripts normais'.

Quando usar:

Este não é um método que eu recomendaria. Corrigir o impacto do JavaScript exigirá muito mais do que apenas mover cada script um pouco mais para o fim da fila. Por outro lado, se você tiver pouco controle sobre os scripts rodando na página ou tiver conhecimento insuficiente de JavaScript, esta pode ser a sua melhor aposta.

Vantagens:

  1. É fácil, basta habilitar o rocket loader ou outro plugin e todos os seus scripts agora são executados em um momento um pouco posterior.
  2. Provavelmente corrigirá as suas métricas de paint, desde que você não tenha usado lazy loading baseado em JS.
  3. Funcionará para scripts inline e externos.

Desvantagens:

  1. Você não terá controle refinado sobre quando os scripts são executados
  2. Scripts mal escritos podem quebrar
  3. Ele usa JavaScript para consertar JavaScript
  4. Ele não faz nada para consertar scripts de longa duração (long running scripts)

Método 8: Usar o intersection observer

Com o intersection observer você pode executar uma função (que neste caso carrega um JavaScript externo) quando um elemento rola (scroll) para o viewport visível.

<script>
const handleIntersection = (entries, observer) => {
    if (entries?.[0].isIntersecting) {
        // carregue seu script ou execute outra
           função como acionar um elemento em lazy load
        loadScript('javascript.js');

        // remova o observer
        observer.unobserve(entries?.[0].target);
    }
};
const Observer = new window.IntersectionObserver()
Observer.observe(document.querySelector('footer'));
</script>

Este é de longe o método mais eficaz de adiar JavaScript que existe. Carregue apenas os scripts que você precisa, pouco antes de precisar deles. Infelizmente, a vida real quase nunca é tão limpa e não muitos scripts podem ser vinculados a um elemento que rola para a visualização.

Quando usar:

Use esta técnica o máximo possível! Sempre que um script interage apenas com um componente fora da tela (como um mapa, um slider, um formulário), esta é a melhor maneira de injetar este script.

Vantagens:

  1. Não interferirá nos Core Web Vitals LCP e FCP
  2. Nunca injetará scripts que não são usados. Isso melhorará o INP

Desvantagens:

  1. Não deve ser usado com componentes que podem estar no viewport visível
  2. É mais difícil de manter do que o básico <script src="...">
  3. Pode introduzir um layout shift

Método 9: Usar o readystatechange

document.readystate pode ser usado como uma alternativa ao evento 'DOMContentloaded' e 'load'. O readystate 'interactive' é geralmente um bom lugar para chamar scripts critical que precisam alterar o DOM ou adicionar event handlers. 
O ready state 'complete' é um bom lugar para chamar scripts que são menos critical.

document.addEventListener('readystatechange', (event) => {
  if (event.target.readyState === 'interactive') {
    initLoader();
  } else if (event.target.readyState === 'complete') {
    initApp();
  }
});

Método 10: Usar setTimeout com nenhum tempo limite (no timeout)

setTimeout é um método reprovado, porém muito subestimado na comunidade de pagespeed. O setTimeout tem má reputação porque é frequentemente mal utilizado.  Muitos desenvolvedores acreditam que o setTimeout só pode ser usado para atrasar a execução do script pela quantidade definida de milissegundos. Embora isso seja verdade, o setTimeout na verdade faz algo muito mais interessante. Ele cria uma nova tarefa no final do event loop do navegador. Esse comportamento pode ser usado para agendar suas tarefas de forma eficaz. Ele também pode ser usado para dividir tarefas longas em tarefas menores e separadas

<script>
   setTimeout(() => {
       // carrega um script ou executa outra função
       console.log('- Eu sou chamado de um timeOut() de 0ms')
    }, 0);

   console.log('- Eu estava no final da fila, mas executei primeiro')
/* Saída: - Eu estava no final da fila, mas executei primeiro - Eu sou chamado de um timeOut() de 0ms */ </script>

Quando usar:

setTimeout cria uma nova tarefa no event loop do navegador. Use isso quando a sua main thread estiver sendo bloqueada por muitas chamadas de função que são executadas sequencialmente.

Vantagens:

  1. Pode dividir código de longa duração em partes menores.

Desvantagens:

  1. setTimeout é um método bastante rudimentar e não oferece priorização para scripts importantes.
  2. Adicionará o código a ser executado no final do loop

Método 11: Usar setTimeout com um tempo limite (timeout)

As coisas ficam ainda mais interessantes quando chamamos setTimeout com um timeout de mais de 0ms

<script>
   setTimeout(() => {
       // carrega um script ou executa outra função
       console.log('- Eu sou chamado de um timeOut() de 10ms')
    }, 10);

   setTimeout(() => {
       // carrega um script ou executa outra função
       console.log('- Eu sou chamado de um timeOut() de 0ms')
    }, 0);

   console.log('- Eu estava no final da fila, mas executei primeiro')
/* Saída: - Eu estava no final da fila, mas executei primeiro - Eu sou chamado de um timeOut() de 0ms - Eu sou chamado de um timeOut() de 10ms */ </script>

Quando usar:

Quando você precisa de um método fácil para agendar um script após o outro, um pequeno timeout resolverá o problema

Vantagens:

  1. Suportado em todos os navegadores

Desvantagens:

  1. Não oferece agendamento avançado

Método 12: Usar uma promise para definir uma microtask

Criar uma micro-task também é uma maneira interessante de agendar JavaScript. Micro-tasks são agendadas para execução imediatamente após a conclusão do loop de execução atual.

<script>
   const myPromise = new Promise((resolve, reject) => {
      resolve();
   }).then(
      () => {
         console.log('- Eu fui agendado após uma promise')
         }
   );
console.log('- Eu estava no final da fila, mas executei primeiro') /* Saída: - Eu estava no final da fila, mas executei primeiro - Eu fui agendado após uma promise */ </script>

Quando usar:

Quando uma tarefa precisa ser agendada imediatamente após outra tarefa. 

Vantagens:

  1. A microtask será agendada imediatamente após a tarefa terminar de ser executada.
  2. Uma microtask pode ser usada para atrasar partes menos importantes de código JavaScript no mesmo evento. 

Desvantagens:

  1. Não dividirá a main thread em partes menores. O navegador não terá chance de responder à entrada do usuário.
  2. Você provavelmente nunca precisará usar microtasks para melhorar os Core Web Vitals, a menos que já saiba exatamente o que está fazendo.

Método 13: Usar uma microtask

O mesmo resultado pode ser alcançado usando queueMicrotask(). A vantagem de usar queueMicrotask() em vez de uma promise é que é um pouco mais rápido e você não precisa lidar com as suas promises.

<script>
   queueMicrotask(() => {
      console.log('- Eu sou uma microtask')
   })

   console.log('- Eu estava no final da fila, mas executei primeiro')

   /*
      Saída:
      - Eu estava no final da fila, mas executei primeiro
      - Eu sou uma microtask
   */
</script>


Método 14: Usar requestIdleCallback

O método window.requestIdleCallback() enfileira uma função para ser chamada durante os períodos de inatividade (idle) do navegador. Isso permite que os desenvolvedores executem trabalho em background e de baixa prioridade no event loop principal, sem impactar eventos sensíveis à latência, como animação e resposta à entrada (input response). As funções são geralmente chamadas na ordem primeiro-a-entrar-primeiro-a-sair (FIFO); no entanto, callbacks que têm um timeout especificado podem ser chamados fora de ordem, se necessário, a fim de executá-los antes que o timeout expire.

<script>
requestIdleCallback(() => {
    const script = document.createElement('script');
    script.src = 'javascript.js';
    document.head.appendChild(script);
});
</script>

Quando usar:

Use isso para scripts que são Nice to have ou para lidar com tarefas não críticas após a entrada do usuário

Vantagens:

  1. Executa JavaScript com impacto mínimo para o usuário
  2. Muito provavelmente melhorará o INP

Desvantagens:

  1. Não há garantia de que o código irá ser disparado alguma vez 

Método 15: Usar postTask

O método scheduler.postTask() permite que os usuários especifiquem opcionalmente um atraso mínimo antes que a tarefa seja executada, uma prioridade para a tarefa, e um sinal que pode ser usado para modificar a prioridade da tarefa e/ou abortar a tarefa. Ele retorna uma promise que é resolvida com o resultado da função de callback da tarefa, ou rejeitada com o motivo do aborto ou um erro gerado na tarefa.

<script>
scheduler.postTask(() => {
    const script = document.createElement('script');
    script.src = 'javascript.js';
    document.head.appendChild(script);
}, { priority: 'background' });
</script>

Quando usar:

postTask é a API certa para agendar scripts quando você precisa de controle refinado sobre a prioridade.

Vantagens:

  1. Controle completo sobre o agendamento da execução do JavaScript!

Desvantagens:

  1. Não é suportado no Safari. Suportado no Chrome 94+, Edge 94+ e Firefox 142+. Use a detecção de recursos (feature detection) e um fallback com setTimeout para cobertura total.

Método 16: Usar scheduler.yield()

scheduler.yield() é a maneira mais recente de dividir tarefas longas. Ele retorna uma promise que resolve em uma nova tarefa, dando ao navegador a chance de responder à entrada do usuário entre partes de trabalho. Diferente do setTimeout, a continuação obtém prioridade sobre outras tarefas na fila, então seu código continua de onde parou sem ser empurrado para o final da fila.

<script>
async function processItems(items) {
    for (const item of items) {
        doWork(item);
        await scheduler.yield();
    }
}
</script>

Esta é a melhor ferramenta individual para melhorar o INP. Tarefas longas que bloqueiam a main thread por centenas de milissegundos podem ser divididas em pedaços menores, cada um separado por um ponto de yield. O navegador pode lidar com a entrada do usuário em cada ponto de yield. Para um passo a passo prático desse padrão, veja como usar o yield na main thread.

O Safari ainda não suporta scheduler.yield(), então inclua sempre um fallback:

<script>
function yieldToMain() {
    if (globalThis.scheduler?.yield) {
        return scheduler.yield();
    }
    return new Promise(resolve => {
        setTimeout(resolve, 0);
    });
}

async function processItems(items) {
    for (const item of items) {
        doWork(item);
        await yieldToMain();
    }
}
</script>

Quando usar:

Use isso sempre que tiver um JavaScript de longa duração que bloqueie a main thread. É a abordagem recomendada para melhorar o INP em manipuladores de interação (interaction handlers) e qualquer código que processe dados em um loop.

Vantagens:

  1. Divide tarefas longas sem perder o seu lugar na fila
  2. A continuação é executada antes de outras tarefas na fila (diferente do setTimeout, que vai para o final da fila)
  3. Melhora diretamente o INP ao dar ao navegador a chance de responder à entrada do usuário

Desvantagens:

  1. Não suportado no Safari. Suportado no Chrome 129+, Edge 129+ e Firefox 142+.
  2. Requer um fallback para cobertura total do navegador (setTimeout funciona como um polyfill)

Depois de aplicar essas técnicas, verifique a melhoria com o Real User Monitoring. As pontuações do Lighthouse são um ponto de partida, mas field data de usuários reais é o que o Google usa para classificação. O CoreDash rastreia o INP e todos os Core Web Vitals de visitantes reais, para que você possa ver se sua estratégia de defer está realmente funcionando em produção.

About the author

Arjen Karel is a web performance consultant and the creator of CoreDash, a Real User Monitoring platform that tracks Core Web Vitals data across hundreds of sites. He also built the Core Web Vitals Visualizer Chrome extension. He has helped clients achieve passing Core Web Vitals scores on over 925,000 mobile URLs.

The RUM tool I built for my own clients.

CoreDash is what I use to audit enterprise platforms. Under 1KB tracking script, EU hosted, no consent banner. AI with MCP support built in. The same tool, available to everyone.

Create Free Account
16 métodos para adiar ou agendar JavaScriptCore Web Vitals 16 métodos para adiar ou agendar JavaScript