16 métodos para adiar ou agendar JavaScript
Aprenda como adiar e agendar JavaScript

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
Table of Contents!
- Por que adiar ou agendar JavaScript?
- Como o tempo do JavaScript pode afetar os Core Web Vitals?
- Como escolher o método defer correto?
- Método 1: Usar o atributo defer
- Método 2: Usar o atributo async
- Método 3: Usar modules
- Método 4: Colocar os scripts perto do final da página
- Método 5: Injetar scripts
- Método 6: Injetar scripts em um momento posterior
- Método 7: Alterar o tipo de script (e depois alterá-lo de volta)
- Método 9: Usar o readystatechange
- Método 10: Usar setTimeout com nenhum tempo limite (no timeout)
- Método 11: Usar setTimeout com um tempo limite (timeout)
- Método 12: Usar uma promise para definir uma microtask
- Método 13: Usar uma microtask
- Método 14: Usar requestIdleCallback
- Método 15: Usar postTask
- Método 16: Usar scheduler.yield()
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.



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.

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 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:
- Scripts deferred serão baixados em paralelo
- O DOM estará disponível no momento da execução
Desvantagens:
- Scripts deferred podem atrasar suas métricas de LCP
- Scripts deferred bloquearão a main thread assim que forem executados
- 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 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:
- Scripts async serão baixados em paralelo.
- Scripts async serão executados o mais rápido possível.
Desvantagens:
- O DOMContentLoaded pode acontecer tanto antes quanto depois do async.
- A ordem de execução dos scripts será desconhecida de antemão.
- 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 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:
- Modules são deferred por padrão
- Modules são mais fáceis de manter e funcionam muito bem com design web modular
- 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:
- Modules por si só não melhorarão os Core Web Vitals
- 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:
- Colocar scripts no final da página não requer muito conhecimento.
- Se feito corretamente, não há risco de quebrar a sua página
Desvantagens:
- Scripts critical podem ser baixados e executados mais tarde
- 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:
- Código contido que injeta um script async.
Desvantagens:
- O DOMContentLoaded pode acontecer tanto antes quanto depois do script ter carregado.
- A ordem de execução dos scripts será desconhecida de antemão.
- 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:
- Não competirá por recursos críticos porque injetará o script assim que a página e seus recursos forem carregados
Desvantagens:
- 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'
- 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 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:
- É 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.
- Provavelmente corrigirá as suas métricas de paint, desde que você não tenha usado lazy loading baseado em JS.
- Funcionará para scripts inline e externos.
Desvantagens:
- Você não terá controle refinado sobre quando os scripts são executados
- Scripts mal escritos podem quebrar
- Ele usa JavaScript para consertar JavaScript
- 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:
- Não interferirá nos Core Web Vitals LCP e FCP
- Nunca injetará scripts que não são usados. Isso melhorará o INP
Desvantagens:
- Não deve ser usado com componentes que podem estar no viewport visível
- É mais difícil de manter do que o básico <script src="...">
- 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:
- Pode dividir código de longa duração em partes menores.
Desvantagens:
- setTimeout é um método bastante rudimentar e não oferece priorização para scripts importantes.
- 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:
- Suportado em todos os navegadores
Desvantagens:
- 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:
- A microtask será agendada imediatamente após a tarefa terminar de ser executada.
- Uma microtask pode ser usada para atrasar partes menos importantes de código JavaScript no mesmo evento.
Desvantagens:
- Não dividirá a main thread em partes menores. O navegador não terá chance de responder à entrada do usuário.
- 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:
- Executa JavaScript com impacto mínimo para o usuário
- Muito provavelmente melhorará o INP
Desvantagens:
- 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:
- Controle completo sobre o agendamento da execução do JavaScript!
Desvantagens:
- 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:
- Divide tarefas longas sem perder o seu lugar na fila
- A continuação é executada antes de outras tarefas na fila (diferente do setTimeout, que vai para o final da fila)
- Melhora diretamente o INP ao dar ao navegador a chance de responder à entrada do usuário
Desvantagens:
- Não suportado no Safari. Suportado no Chrome 129+, Edge 129+ e Firefox 142+.
- 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.
Search Console flagged your site?
When Google flags your Core Web Vitals you need a clear diagnosis fast. I deliver a prioritized fix list within 48 hours.
Request Urgent Audit
