Linguagem e serviços do navegador
Promises, funções e objetos pertencem à linguagem; DOM, eventos, fetch e AbortController são serviços do ambiente de navegador usados nesta aula. Uma função JavaScript válida pode depender de APIs que outro ambiente não fornece. Declara o contexto antes de interpretar um exemplo e não concluas compatibilidade de todos os navegadores apenas porque uma edição de ECMAScript está publicada.
Um pedido HTTP tem várias fronteiras observáveis. Fetch produz uma Response quando recebe a resposta inicial acessível; isso não significa que o corpo já foi totalmente lido nem que os dados são válidos para a aplicação. Uma rejeição de rede, um estado HTTP de erro, JSON malformado e um campo obrigatório ausente são resultados diferentes. Mantém essas distinções no diagnóstico, mesmo que a interface apresente uma mensagem curta comum.
Identidade do listener e do evento
Num botão com um ícone descendente, um click no ícone pode ter esse descendente como target, enquanto currentTarget identifica o botão cujo listener está a executar. Esta distinção evita aplicar uma ação ao elemento errado. currentTarget só tem o significado do listener durante a sua execução; se precisas da referência mais tarde, guarda o elemento numa variável enquanto o handler está ativo. Conservar o objeto event não fixa todas as suas propriedades transitórias.
Para remover um listener, conserva a identidade da função e usa o mesmo tipo de evento e valor de capture. Uma nova arrow com o mesmo corpo continua a ser outra função. Um listener registado com capture true não é removido pela variante false. A remoção não exige que todas as outras opções, como passive, sejam repetidas; não confundas a configuração inicial com os critérios de correspondência usados na remoção.
Ação por omissão e apresentação textual
preventDefault trata a ação por omissão de um evento cancelável, desde que o listener não seja passivo. Não remove o listener e não para, por si só, a propagação para outros listeners. Um problema de navegação ou submissão não se resolve automaticamente com uma regra de propagação, e um problema de listeners duplicados não se resolve cancelando a ação por omissão. Identifica primeiro que efeito está a ocorrer.
Para mostrar um rótulo externo como texto literal, textContent evita interpretar a string como marcação HTML. Atribuir "<b>Ready</b>" deve mostrar esses caracteres quando esse é o contrato. Esta atribuição substitui os filhos anteriores do elemento, pelo que o destino deve ser escolhido com cuidado. Não é um mecanismo universal para validar URLs, regras de negócio ou outros contextos de saída; cumpre a tarefa delimitada de apresentar texto.
Estado HTTP, corpo e contrato de dados
Uma resposta 404 normal não rejeita automaticamente a promise de fetch. Verifica response.ok ou o estado específico que o contrato permite. response.ok cobre os estados de sucesso 200–299; não demonstra que o corpo tem os campos necessários. response.json() é outra operação assíncrona: pode falhar na leitura ou na sintaxe e, quando tem sucesso, ainda pode produzir null, um array ou outro valor que a aplicação não aceita.
O corpo é um fluxo consumível. Depois de uma leitura bem-sucedida de um corpo JSON não nulo, chamar json() novamente no mesmo Response não fornece uma cache do objeto. Retém o valor já interpretado ou, se precisas de consumidores independentes, prepara um clone antes de consumir. Também não uses no-cors como solução para ler uma resposta bloqueada: uma resposta opaca não disponibiliza ao script o corpo e os cabeçalhos que este exemplo exige.
Cancelar e decidir se um resultado ainda é corrente
AbortController permite pedir cancelamento a operações que recebem o seu signal. Esse pedido pode fazer falhar fetch ou a leitura do corpo, mesmo depois de a Response inicial ter chegado. Sem motivo personalizado, o caso normal de cancelamento usa AbortError. A observação é local: não prova que o servidor nunca começou o trabalho nem que desfez uma escrita já aceite. Se o resultado de uma criação é desconhecido, usa o contrato de identidade e consulta do serviço antes de decidir repetir.
Numa pesquisa, cancelar a consulta antiga pode reduzir trabalho inútil, mas a publicação também precisa de uma guarda de identidade. Dá a cada consulta uma geração crescente e compara-a com a corrente imediatamente antes de alterar a interface. A resposta que chega por último pode ser antiga. Aplica a mesma guarda ao caminho de erro: uma falha antiga também não deve substituir resultados atuais por uma mensagem de indisponibilidade.
Exemplo orientado: um rótulo corrente
createLabelLoader cria estado privado para um elemento. Cada load incrementa generation, pede cancelamento ao pedido anterior e guarda um novo controller. mine retém a geração desta chamada. As verificações HTTP e de esquema antecedem a publicação do rótulo. O contrato aceita uma string com até 200 unidades UTF-16, incluindo a string vazia; não está a contar caracteres visuais. textContent apresenta o rótulo literalmente.
Segue A com mine 1 e B com mine 2. Se B já começou, A não pode publicar nem no sucesso nem no catch, porque a geração corrente é 2. O finally de A também não pode limpar active: essa referência já pode pertencer a B. A guarda no finally preserva a capacidade de uma chamada seguinte cancelar B. Não há await entre a última comparação de geração e a atribuição de texto, no contexto DOM comum indicado.
Limites do exemplo e diagnóstico útil
O exemplo é um adaptador de apresentação para GETs autorizados, não uma função genérica que devolve dados de negócio. O seu catch converte uma falha corrente em "Unavailable"; a promise de load não certifica sucesso de obtenção de dados. Numa aplicação que precisa de classificar falhas, preserva estado explícito ou diagnóstico interno adequado. O rótulo curto, por si só, não distingue rede, HTTP, JSON e esquema.
Antes de alterar o código, reconstrói a sequência: consulta escolhida, geração, pedido iniciado, cabeçalhos, corpo, validação e decisão de publicação. Não introduzas atrasos fixos para esconder uma corrida. Se o problema envolve escrita remota, o exemplo de rótulo não fornece idempotência nem rollback; usa o contrato específico do serviço. As perguntas avaliam estas decisões através de excertos e cenários, sem exigir execução no navegador.
function createLabelLoader(label) {
let generation = 0;
let active = null;
return async function load(url) {
const mine = ++generation;
if (active !== null) active.abort();
const controller = new AbortController();
active = controller;
label.textContent = "Loading";
try {
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error("HTTP " + response.status);
const data = await response.json();
if (data === null || typeof data !== "object" || Array.isArray(data) ||
typeof data.label !== "string" || data.label.length > 200) {
throw new TypeError("label schema");
}
if (mine !== generation) return;
label.textContent = data.label;
} catch (error) {
if (mine !== generation) return;
label.textContent = "Unavailable";
} finally {
if (mine === generation) active = null;
}
};
}O elemento label é um elemento DOM comum já existente e destinado apenas a texto. Os URLs são GETs de leitura autorizados da aplicação. O código usa APIs padrão de navegador.
Considera duas chamadas consecutivas, A e B, cujas respostas podem terminar fora de ordem; acompanha mine, generation e active.
Armadilhas comuns
Remover listeners com outra função; assumir que preventDefault para propagação; ler o corpo duas vezes; usar innerHTML para texto externo; tratar abort como confirmação de ausência de efeitos.
Tópicos relacionados: Erros, validação e contratos de falha · JSON, texto e contratos de tempo · Promises, async e propagação de erros · Estado, ordem e diagnóstico de integração
Cada fronteira tem um resultado próprio. Valida a resposta e a sua identidade antes de publicar; o cancelamento local não descreve sozinho o estado remoto.
Referência: Using Fetch · ECMAScript 2026 (17th edition); browser APIs identified separately