Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

AsyncResource no Node.js: Guia Prático

Atualizado em: 16 de agosto de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

O AsyncResource no Node.js permite representar manualmente uma operação assíncrona para que Async Hooks, AsyncLocalStorage e ferramentas de observabilidade mantenham a relação correta entre criação, execução e encerramento. Ele é útil em pools de tarefas, filas, callbacks nativos, bibliotecas e abstrações que executam trabalho fora do fluxo assíncrono reconhecido automaticamente pelo runtime.

Sem AsyncResource, um callback disparado por uma fila personalizada pode perder o contexto da requisição que o criou. Isso afeta IDs de correlação, tracing, logs e métricas. Ao executar o callback dentro do escopo do recurso, a aplicação restaura a causalidade esperada.

Neste guia, você aprenderá a criar recursos, usar runInAsyncScope(), trabalhar com AsyncLocalStorage, emitir destroy, criar pools, vincular funções, tratar erros, evitar vazamentos e testar propagação de contexto.

O que é AsyncResource?

AsyncResource é uma classe do módulo node:async_hooks. A documentação oficial de AsyncResource no Node.js descreve construtor, métodos e exemplos. O guia oficial de Async Hooks apresenta IDs e ciclo de vida.

Para contexto por requisição, consulte AsyncLocalStorage no Node.js. Para eventos do runtime, veja Async Hooks no Node.js. O artigo de Event Loop no Node.js ajuda a entender a execução dos callbacks.

Criando um recurso

const {
  AsyncResource
} = require('node:async_hooks');

const resource = new AsyncResource('CUSTOM_TASK');

O nome identifica o tipo nas ferramentas de diagnóstico. Use uma constante estável e específica.

runInAsyncScope()

resource.runInAsyncScope(
  callback,
  thisArg,
  argument1,
  argument2
);

O método executa a função como se ela pertencesse ao contexto assíncrono do recurso.

Exemplo básico

function schedule(callback) {
  const resource = new AsyncResource('SCHEDULED_CALLBACK');

  setImmediate(() => {
    try {
      resource.runInAsyncScope(callback);
    } finally {
      resource.emitDestroy();
    }
  });
}

Mesmo que setImmediate() já seja rastreado, o exemplo mostra como associar o callback ao recurso lógico criado no momento do agendamento.

AsyncLocalStorage

const {
  AsyncLocalStorage
} = require('node:async_hooks');

const storage = new AsyncLocalStorage();

storage.run({ requestId: 'req-123' }, () => {
  queue.add(() => {
    console.log(storage.getStore());
  });
});

Uma fila mal implementada pode executar o callback em um contexto diferente. AsyncResource ajuda a preservar o contexto da chamada add().

Fila com contexto

class Task {
  constructor(callback) {
    this.callback = callback;
    this.resource = new AsyncResource('TASK_QUEUE_ITEM');
  }

  run() {
    return this.resource.runInAsyncScope(
      this.callback,
      null
    );
  }

  destroy() {
    this.resource.emitDestroy();
  }
}

Executando a fila

async function processTask(task) {
  try {
    return await task.run();
  } finally {
    task.destroy();
  }
}

O finally garante encerramento mesmo quando o callback falha.

Recurso por tarefa

Crie um AsyncResource para cada unidade lógica. Reutilizar um único recurso para tarefas de requisições diferentes mistura causalidade e contexto.

Pool de Worker Threads

Um pool pode criar um recurso para cada job enviado ao worker. Quando a resposta retorna, o callback é executado no escopo do job original.

class WorkerJob extends AsyncResource {
  constructor(callback) {
    super('WORKER_POOL_JOB');
    this.callback = callback;
  }

  complete(error, value) {
    try {
      this.runInAsyncScope(
        this.callback,
        null,
        error,
        value
      );
    } finally {
      this.emitDestroy();
    }
  }
}

Veja Worker Threads no Node.js para pools e mensagens.

triggerAsyncId

O construtor pode receber opções:

const resource = new AsyncResource(
  'CUSTOM_TASK',
  {
    triggerAsyncId: executionAsyncId(),
    requireManualDestroy: true
  }
);

O triggerAsyncId indica qual recurso causou a criação. Normalmente o valor padrão já representa o contexto atual.

requireManualDestroy

Quando ativado, você assume responsabilidade por chamar emitDestroy(). Isso melhora precisão do ciclo de vida, mas esquecer a chamada prejudica ferramentas e pode manter metadados.

emitDestroy()

resource.emitDestroy();

Chame uma vez quando o recurso não produzirá mais callbacks. Não emita destroy antes da conclusão.

asyncId()

console.log(resource.asyncId());

O ID pode ajudar em diagnósticos, mas não deve ser usado como identificador persistente ou regra de autorização.

triggerAsyncId()

console.log(resource.triggerAsyncId());

O valor conecta o recurso ao causador conhecido pelo runtime.

bind()

AsyncResource oferece formas de vincular uma função ao contexto:

const bound = resource.bind(callback);
setImmediate(bound);

A disponibilidade e assinatura dependem da versão. Prefira um wrapper explícito quando precisa controlar destroy.

AsyncResource.bind()

A classe também pode oferecer método estático para vincular a função ao contexto atual:

const bound = AsyncResource.bind(callback);

Teste com a versão mínima do projeto.

thisArg e argumentos

resource.runInAsyncScope(
  handler,
  service,
  event
);

O segundo argumento define this. Os demais são encaminhados à função.

Promises retornadas

runInAsyncScope() retorna o valor da função. Se ela retorna uma Promise, o chamador precisa aguardar e capturar rejeições.

await resource.runInAsyncScope(
  async () => performTask()
);

Destroy após Promise

try {
  await resource.runInAsyncScope(callback);
} finally {
  resource.emitDestroy();
}

Não emita destroy imediatamente depois de obter a Promise sem aguardá-la, quando o recurso lógico inclui todo o trabalho assíncrono.

Erros síncronos

Erros lançados atravessam runInAsyncScope(). Use try/finally para limpeza e deixe a camada apropriada decidir como responder.

Callbacks múltiplos

Alguns recursos geram vários eventos, como uma assinatura. Mantenha o recurso vivo até cancelar a assinatura, então emita destroy.

EventEmitter personalizado

Um emissor que representa conexão persistente pode usar um AsyncResource por conexão, mas callbacks de eventos independentes podem exigir recursos próprios conforme a semântica.

Não envolva tudo

Promises, timers, filesystem, sockets e outras APIs nativas já são rastreadas. Use AsyncResource somente quando sua abstração quebra ou redefine a relação assíncrona.

Bibliotecas

Bibliotecas de pools, schedulers e bridges nativas devem considerar AsyncResource para interoperar com ferramentas de contexto sem exigir configuração do usuário.

Node-API

Addons nativos que chamam JavaScript de threads externas podem precisar criar escopo assíncrono por meio de APIs equivalentes. Consulte Node-API no Node.js quando disponível.

Observabilidade

AsyncResource melhora:

  • IDs de correlação;
  • traces;
  • logs por requisição;
  • diagnóstico com Async Hooks;
  • atribuição de tarefas em pools;
  • propagação de contexto.

OpenTelemetry

Instrumentações de tracing dependem de contexto assíncrono correto. Veja OpenTelemetry no Node.js.

Performance

Criar recursos e executar hooks possui custo. Meça em filas de alto volume. Use nomes de tipo estáveis e não ative hooks detalhados sem necessidade.

Vazamentos

Maps de tarefas, listeners e recursos nunca destruídos podem crescer. O problema não é apenas o AsyncResource; toda estrutura associada ao job precisa ser removida.

Timeouts

Uma tarefa que nunca completa deve expirar:

const timer = setTimeout(() => {
  job.complete(new Error('Timeout'));
}, 5000);

job.onComplete = () => clearTimeout(timer);

Garanta que complete seja idempotente.

Cancelamento

Associe um AbortSignal ao job e finalize o recurso quando o cancelamento for confirmado. Veja AbortController no Node.js.

Testando contexto

test('preserva requestId na fila', async () => {
  await storage.run(
    { requestId: 'test-1' },
    async () => {
      const value = await queue.execute(() => {
        return storage.getStore().requestId;
      });

      assert.equal(value, 'test-1');
    }
  );
});

Testando erros

Verifique se o recurso é destruído, o job é removido da fila e a Promise é rejeitada quando o callback lança erro.

Testando concorrência

Execute várias tarefas com requestIds diferentes e confirme que os contextos não se misturam.

Erros comuns

  • Um recurso para todas as tarefas: contextos se misturam.
  • Esquecer emitDestroy: o ciclo fica incompleto.
  • Destroy cedo demais: operações posteriores perdem relação.
  • Não aguardar Promise: limpeza acontece antes da conclusão.
  • Envolver APIs nativas sem necessidade: overhead aumenta.
  • Sem timeout: jobs pendentes acumulam.
  • Usar asyncId como ID de negócio: o valor não é persistente.

Boas práticas

  • Crie um recurso por operação lógica.
  • Use nome estável.
  • Execute callbacks com runInAsyncScope.
  • Emita destroy no momento correto.
  • Use try/finally.
  • Aguarde Promises.
  • Limite filas.
  • Aplique timeouts.
  • Teste contextos concorrentes.
  • Meça overhead.

Conclusão

O AsyncResource no Node.js permite que abstrações assíncronas personalizadas mantenham causalidade e contexto compatíveis com Async Hooks e AsyncLocalStorage.

Ele é especialmente valioso em pools, filas e bridges nativas. O uso correto exige um recurso por tarefa, execução dentro do escopo, destroy no momento adequado e limites para jobs pendentes. Com essas regras, logs e traces continuam ligados à requisição certa mesmo quando o trabalho passa por uma abstração personalizada.

Os 10 Melhores Cursos de Programação de 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita