[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M786DF1R7PCK6KB44B34110S":3,"skill-same:01M786DF1R7PCK6KB44B34110S":47},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":18,"linkOnly":19,"markdownContent":20,"markdownBody":21,"frontMatter":22,"refName":23,"commitSha":24,"skillDirectory":25,"forksCount":26,"repositoryHtmlUrl":27,"repositoryHomepageUrl":28,"ownerHtmlUrl":29,"ownerBlogUrl":30,"ownerDescription":31,"updatedAt":32,"install":33,"resources":46},"01M786DF1R7PCK6KB44B34110S","ts-async-patterns",null,"allanpablo\u002Fforja","allanpablo","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F22358346?v=4","01M786AH79M6N27CC4HZ2AHJTQ","Allan Pablo","01M786AH80CPCTFFMSTRMGEGYF",2,"MIT","https:\u002F\u002Fgithub.com\u002Fallanpablo\u002Fforja\u002Fblob\u002Fmain\u002Ftemplates\u002Fproject\u002Fskills\u002Fts-async-patterns\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: ts-async-patterns\ndescription: Use quando código TypeScript ou JavaScript precisa de concorrência ou tem bugs assíncronos (serviços Node, workers, scripts): decide se a concorrência é necessária e então orienta a posse das promises, cancelamento e timeouts com AbortSignal, concorrência limitada, backpressure de streams, worker threads para trabalho de CPU e desligamento limpo, com um checklist de vazamentos.\n---\n\n# Padrões Assíncronos em TypeScript\n\nO Node roda seu código numa thread só e faz I\u002FO de forma concorrente. Isso torna a concorrência fácil de começar e fácil de errar: um `Promise.all` sobre dez mil itens abre dez mil conexões, uma promise esquecida rejeita sem ninguém ouvindo, e um `setInterval` mantém o processo vivo depois do desligamento. Toda operação assíncrona precisa de um dono que a aguarde e de uma forma de pará-la.\n\nA saída padrão é uma **escolha de padrão com a justificativa e um checklist**. Escreva código só quando o usuário pedir.\n\n## Convenções\n\n- Entradas: metas de vazão e latência de `requirements-gathering` \u002F `observability`, limites de taxa dos serviços a jusante, o código existente.\n- Os padrões já existentes no código prevalecem. APIs da plataforma primeiro (`AbortController`, `node:stream\u002Fpromises`, `node:timers\u002Fpromises`, `node:worker_threads`), depois bibliotecas pequenas e focadas (`p-limit`, `p-map`, `piscina`) quando o projeto permite dependências.\n- Promises soltas são pegas pelo linter (`@typescript-eslint\u002Fno-floating-promises` ou o `typescript\u002Fno-floating-promises` do oxlint); veja `ts-code-review`.\n\n## Decida primeiro\n\nA concorrência se justifica quando o trabalho é **independente** e limitado por I\u002FO (chamadas, consultas, arquivos). Trabalho limitado por CPU não fica mais rápido com mais promises: ele bloqueia o event loop e precisa de worker threads ou de outro processo. \"Deixar mais rápido\" sem uma medição não é motivo. `for … of` sequencial com `await` é o padrão.\n\n## Regras\n\n- **Posse**: a função que inicia o trabalho assíncrono o aguarda ou o entrega a alguém que aguarda. Nada de \"dispara e esquece\" em caminhos de requisição. Uma tarefa de fundo deliberada passa por uma fila ou por um supervisor que registra e tenta de novo.\n- **Cancelamento**: toda operação longa ou de saída aceita um `AbortSignal` e o repassa (`fetch(url, { signal })`, drivers de banco, `setTimeout` de `node:timers\u002Fpromises`). Combine sinais com `AbortSignal.any([pai, AbortSignal.timeout(ms)])`.\n- **Prazos**: toda chamada de saída tem um timeout derivado do orçamento de quem chama. `fetch` não tem timeout padrão.\n- **Concorrência limitada**: nunca `Promise.all(items.map(fn))` sobre uma entrada sem limite. Use um limite (`p-limit`, `p-map` com `concurrency`, ou um pequeno pool de workers).\n- **Erros**: toda promise tem um tratamento. Escolha de propósito entre `Promise.all` (falha rápido; as outras continuam rodando, a menos que você as aborte) e `Promise.allSettled` (coleta todos os resultados). Uma rejeição sem tratamento derruba o processo Node por padrão; isso está certo, então não a silencie globalmente.\n- **Estado compartilhado**: o event loop torna atômicas as instruções isoladas, não as sequências que atravessam um `await`. Um ler-alterar-escrever que atravessa um `await` precisa de um lock, uma fila ou uma transação.\n- **Recursos**: libere em `finally` ou com `await using` (gerenciamento explícito de recursos) para conexões, handles de arquivo e locks. Remova os listeners e limpe os timers que você criou.\n- **Nunca bloqueie o loop**: nada de criptografia ou compressão síncrona, `JSON.parse` grande ou APIs de arquivo `*Sync` em caminhos de requisição.\n\n## Padrões\n\n| Necessidade | Padrão |\n|---|---|\n| Rodar N tarefas independentes, falhar rápido no primeiro erro | `Promise.all` com limite, mais um `AbortController` abortado na primeira falha |\n| Rodar N tarefas e reportar todos os resultados | `Promise.allSettled` com limite; mapeie os resultados por índice |\n| Processar um fluxo de jobs com paralelismo fixo | Pool de workers limitado (`p-limit` \u002F `p-map`) ou uma fila com N consumidores |\n| Transformar dados em etapas | `node:stream` + `pipeline()` de `node:stream\u002Fpromises`, ou geradores assíncronos consumidos com `for await` |\n| Chamar vários backends e juntar os resultados | Fan-out com limite e `AbortSignal.timeout` por chamada; colete num array indexado pela posição |\n| Trabalho limitado por CPU | `worker_threads` (um pool como `piscina`), nunca na thread principal |\n| Limitar a taxa de chamadas a uma dependência | Token bucket ou o limitador do provedor; respeite `Retry-After` |\n| Evitar trabalho concorrente duplicado | Memorize a promise em andamento por chave (single-flight) e limpe-a ao concluir |\n| Tentar de novo uma falha transitória | Backoff exponencial com jitter e um número máximo de tentativas, só para operações idempotentes |\n\n## Backpressure\n\nQuando os produtores são mais rápidos que os consumidores, escolha de propósito:\n\n- **Bloquear**: respeite o `write()` devolvendo `false` e espere o `'drain'`, ou use `pipeline()`, que faz isso por você. Padrão para pipelines internos.\n- **Rejeitar**: devolva `429`\u002F`503` quando a fila estiver cheia. Para serviços guiados por requisições.\n- **Descartar ou amostrar**: para dados do tipo telemetria, em que a perda é aceitável. Conte os descartes como métrica.\n\nNunca use um array sem limite como fila: ele transforma sobrecarga em estouro de heap.\n\n## Desligamento limpo\n\nEm `SIGTERM`\u002F`SIGINT`: pare de aceitar trabalho (`server.close()`, depois `server.closeIdleConnections()`), aborte o `AbortController` raiz, espere o trabalho em andamento com um prazo, descarregue e feche os recursos (pool do banco, consumidores de fila, telemetria) e então saia. Registre os handlers uma vez, na raiz de composição. Os frameworks oferecem ganchos para isso (`app.enableShutdownHooks()` no NestJS, `onClose` no Fastify).\n\n## Checklist\n\n- [ ] Toda promise é aguardada, devolvida ou explicitamente possuída por um supervisor.\n- [ ] Toda operação longa ou de saída recebe um `AbortSignal` e tem timeout.\n- [ ] A concorrência sobre coleções é limitada.\n- [ ] Grupos que falham rápido abortam o trabalho restante no primeiro erro.\n- [ ] Nenhum ler-alterar-escrever de estado compartilhado atravessa um `await` sem lock ou transação.\n- [ ] Nenhum trabalho pesado de CPU ou `*Sync` no event loop em caminhos de requisição.\n- [ ] Listeners, intervals e timers criados são removidos no desligamento.\n- [ ] O desligamento escoa o trabalho em andamento dentro de um prazo.\n- [ ] Os testes usam timers falsos (`vi.useFakeTimers()`, `mock.timers` no `node:test`) ou relógios injetados em vez de esperas reais.\n\n## Formato\n\n```\n## Problema\n\u003Ccarga, tamanho da entrada, metas, limitado por I\u002FO ou por CPU>\n\n## Decisão\n\u003Csequencial | padrão>, porque \u003Cmotivo>. Limite: \u003Cn>, a partir de \u003Cmedição ou limite a jusante>.\nCancelamento\u002Ftimeouts: \u003Csinais e orçamentos>. Backpressure: \u003Cbloquear | rejeitar | descartar>. Desligamento: \u003Csequência>.\n\n## Checklist\n\u003Citens com ✅\u002F❌ para o código existente>\n```\n\n## Próximo\n\n- `ts-testing` para testes com timers falsos e de caminhos de aborto.\n- `ts-code-review` verifica o checklist na implementação.\n- `observability`: atraso do event loop, profundidade da fila, quantidade em andamento e descartes como métricas.\n\n## Evite\n\n- `Promise.all` sobre entrada sem limite.\n- Callbacks `async` passados ao `forEach` (ninguém os aguarda).\n- Promises iniciadas e nunca aguardadas em caminhos de requisição.\n- Esperas baseadas em `setTimeout` para sincronização.\n- Engolir erros com `.catch(() => {})`, ou um handler global de `unhandledRejection` que só registra.\n- Trabalho limitado por CPU na thread principal.\n- Adicionar concorrência sem uma medição mostrando que ajuda.\n","# Padrões Assíncronos em TypeScript\n\nO Node roda seu código numa thread só e faz I\u002FO de forma concorrente. Isso torna a concorrência fácil de começar e fácil de errar: um `Promise.all` sobre dez mil itens abre dez mil conexões, uma promise esquecida rejeita sem ninguém ouvindo, e um `setInterval` mantém o processo vivo depois do desligamento. Toda operação assíncrona precisa de um dono que a aguarde e de uma forma de pará-la.\n\nA saída padrão é uma **escolha de padrão com a justificativa e um checklist**. Escreva código só quando o usuário pedir.\n\n## Convenções\n\n- Entradas: metas de vazão e latência de `requirements-gathering` \u002F `observability`, limites de taxa dos serviços a jusante, o código existente.\n- Os padrões já existentes no código prevalecem. APIs da plataforma primeiro (`AbortController`, `node:stream\u002Fpromises`, `node:timers\u002Fpromises`, `node:worker_threads`), depois bibliotecas pequenas e focadas (`p-limit`, `p-map`, `piscina`) quando o projeto permite dependências.\n- Promises soltas são pegas pelo linter (`@typescript-eslint\u002Fno-floating-promises` ou o `typescript\u002Fno-floating-promises` do oxlint); veja `ts-code-review`.\n\n## Decida primeiro\n\nA concorrência se justifica quando o trabalho é **independente** e limitado por I\u002FO (chamadas, consultas, arquivos). Trabalho limitado por CPU não fica mais rápido com mais promises: ele bloqueia o event loop e precisa de worker threads ou de outro processo. \"Deixar mais rápido\" sem uma medição não é motivo. `for … of` sequencial com `await` é o padrão.\n\n## Regras\n\n- **Posse**: a função que inicia o trabalho assíncrono o aguarda ou o entrega a alguém que aguarda. Nada de \"dispara e esquece\" em caminhos de requisição. Uma tarefa de fundo deliberada passa por uma fila ou por um supervisor que registra e tenta de novo.\n- **Cancelamento**: toda operação longa ou de saída aceita um `AbortSignal` e o repassa (`fetch(url, { signal })`, drivers de banco, `setTimeout` de `node:timers\u002Fpromises`). Combine sinais com `AbortSignal.any([pai, AbortSignal.timeout(ms)])`.\n- **Prazos**: toda chamada de saída tem um timeout derivado do orçamento de quem chama. `fetch` não tem timeout padrão.\n- **Concorrência limitada**: nunca `Promise.all(items.map(fn))` sobre uma entrada sem limite. Use um limite (`p-limit`, `p-map` com `concurrency`, ou um pequeno pool de workers).\n- **Erros**: toda promise tem um tratamento. Escolha de propósito entre `Promise.all` (falha rápido; as outras continuam rodando, a menos que você as aborte) e `Promise.allSettled` (coleta todos os resultados). Uma rejeição sem tratamento derruba o processo Node por padrão; isso está certo, então não a silencie globalmente.\n- **Estado compartilhado**: o event loop torna atômicas as instruções isoladas, não as sequências que atravessam um `await`. Um ler-alterar-escrever que atravessa um `await` precisa de um lock, uma fila ou uma transação.\n- **Recursos**: libere em `finally` ou com `await using` (gerenciamento explícito de recursos) para conexões, handles de arquivo e locks. Remova os listeners e limpe os timers que você criou.\n- **Nunca bloqueie o loop**: nada de criptografia ou compressão síncrona, `JSON.parse` grande ou APIs de arquivo `*Sync` em caminhos de requisição.\n\n## Padrões\n\n| Necessidade | Padrão |\n|---|---|\n| Rodar N tarefas independentes, falhar rápido no primeiro erro | `Promise.all` com limite, mais um `AbortController` abortado na primeira falha |\n| Rodar N tarefas e reportar todos os resultados | `Promise.allSettled` com limite; mapeie os resultados por índice |\n| Processar um fluxo de jobs com paralelismo fixo | Pool de workers limitado (`p-limit` \u002F `p-map`) ou uma fila com N consumidores |\n| Transformar dados em etapas | `node:stream` + `pipeline()` de `node:stream\u002Fpromises`, ou geradores assíncronos consumidos com `for await` |\n| Chamar vários backends e juntar os resultados | Fan-out com limite e `AbortSignal.timeout` por chamada; colete num array indexado pela posição |\n| Trabalho limitado por CPU | `worker_threads` (um pool como `piscina`), nunca na thread principal |\n| Limitar a taxa de chamadas a uma dependência | Token bucket ou o limitador do provedor; respeite `Retry-After` |\n| Evitar trabalho concorrente duplicado | Memorize a promise em andamento por chave (single-flight) e limpe-a ao concluir |\n| Tentar de novo uma falha transitória | Backoff exponencial com jitter e um número máximo de tentativas, só para operações idempotentes |\n\n## Backpressure\n\nQuando os produtores são mais rápidos que os consumidores, escolha de propósito:\n\n- **Bloquear**: respeite o `write()` devolvendo `false` e espere o `'drain'`, ou use `pipeline()`, que faz isso por você. Padrão para pipelines internos.\n- **Rejeitar**: devolva `429`\u002F`503` quando a fila estiver cheia. Para serviços guiados por requisições.\n- **Descartar ou amostrar**: para dados do tipo telemetria, em que a perda é aceitável. Conte os descartes como métrica.\n\nNunca use um array sem limite como fila: ele transforma sobrecarga em estouro de heap.\n\n## Desligamento limpo\n\nEm `SIGTERM`\u002F`SIGINT`: pare de aceitar trabalho (`server.close()`, depois `server.closeIdleConnections()`), aborte o `AbortController` raiz, espere o trabalho em andamento com um prazo, descarregue e feche os recursos (pool do banco, consumidores de fila, telemetria) e então saia. Registre os handlers uma vez, na raiz de composição. Os frameworks oferecem ganchos para isso (`app.enableShutdownHooks()` no NestJS, `onClose` no Fastify).\n\n## Checklist\n\n- [ ] Toda promise é aguardada, devolvida ou explicitamente possuída por um supervisor.\n- [ ] Toda operação longa ou de saída recebe um `AbortSignal` e tem timeout.\n- [ ] A concorrência sobre coleções é limitada.\n- [ ] Grupos que falham rápido abortam o trabalho restante no primeiro erro.\n- [ ] Nenhum ler-alterar-escrever de estado compartilhado atravessa um `await` sem lock ou transação.\n- [ ] Nenhum trabalho pesado de CPU ou `*Sync` no event loop em caminhos de requisição.\n- [ ] Listeners, intervals e timers criados são removidos no desligamento.\n- [ ] O desligamento escoa o trabalho em andamento dentro de um prazo.\n- [ ] Os testes usam timers falsos (`vi.useFakeTimers()`, `mock.timers` no `node:test`) ou relógios injetados em vez de esperas reais.\n\n## Formato\n\n```\n## Problema\n\u003Ccarga, tamanho da entrada, metas, limitado por I\u002FO ou por CPU>\n\n## Decisão\n\u003Csequencial | padrão>, porque \u003Cmotivo>. Limite: \u003Cn>, a partir de \u003Cmedição ou limite a jusante>.\nCancelamento\u002Ftimeouts: \u003Csinais e orçamentos>. Backpressure: \u003Cbloquear | rejeitar | descartar>. Desligamento: \u003Csequência>.\n\n## Checklist\n\u003Citens com ✅\u002F❌ para o código existente>\n```\n\n## Próximo\n\n- `ts-testing` para testes com timers falsos e de caminhos de aborto.\n- `ts-code-review` verifica o checklist na implementação.\n- `observability`: atraso do event loop, profundidade da fila, quantidade em andamento e descartes como métricas.\n\n## Evite\n\n- `Promise.all` sobre entrada sem limite.\n- Callbacks `async` passados ao `forEach` (ninguém os aguarda).\n- Promises iniciadas e nunca aguardadas em caminhos de requisição.\n- Esperas baseadas em `setTimeout` para sincronização.\n- Engolir erros com `.catch(() => {})`, ou um handler global de `unhandledRejection` que só registra.\n- Trabalho limitado por CPU na thread principal.\n- Adicionar concorrência sem uma medição mostrando que ajuda.\n",{},"main","cfe29d604e93a4c19fd25db4289328f6ec17bb41","templates\u002Fproject\u002Fskills\u002Fts-async-patterns",0,"https:\u002F\u002Fgithub.com\u002Fallanpablo\u002Fforja","npmjs.com\u002Fforjajs","https:\u002F\u002Fgithub.com\u002Fallanpablo","www.allanpablo.com.br","Gestor de TI","2026-11-15 05:12:58",{"source":7,"skillName":5,"commands":34,"prompt":44,"zipAvailable":45},[35,38,41],{"tool":36,"command":37},"npx","npx skills add allanpablo\u002Fforja --skill ts-async-patterns",{"tool":39,"command":40},"bunx","bunx skills add allanpablo\u002Fforja --skill ts-async-patterns",{"tool":42,"command":43},"pnpm","pnpm dlx skills add allanpablo\u002Fforja --skill ts-async-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fallanpablo\u002Fforja\u002Fblob\u002Fmain\u002Ftemplates\u002Fproject\u002Fskills\u002Fts-async-patterns\u002FSKILL.md",true,[],[48,54,60,66,72,78],{"skillNo":49,"name":50,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":51,"categories":52,"keywords":53,"publishedAt":18},"01M786DTCHHV8K0MZMQQA2N81Q","web-performance","https:\u002F\u002Fgithub.com\u002Fallanpablo\u002Fforja\u002Fblob\u002Fmain\u002Ftemplates\u002Fproject\u002Fskills\u002Fweb-performance\u002FSKILL.md",[],[],{"skillNo":55,"name":56,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":57,"categories":58,"keywords":59,"publishedAt":18},"01M786DQ9FDGFMJTF6WANNJ7QG","ts-testing","https:\u002F\u002Fgithub.com\u002Fallanpablo\u002Fforja\u002Fblob\u002Fmain\u002Ftemplates\u002Fproject\u002Fskills\u002Fts-testing\u002FSKILL.md",[],[],{"skillNo":61,"name":62,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":63,"categories":64,"keywords":65,"publishedAt":18},"01M786DMASM9PXP0NSEZYTHA7Q","ts-module-layout","https:\u002F\u002Fgithub.com\u002Fallanpablo\u002Fforja\u002Fblob\u002Fmain\u002Ftemplates\u002Fproject\u002Fskills\u002Fts-module-layout\u002FSKILL.md",[],[],{"skillNo":67,"name":68,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":69,"categories":70,"keywords":71,"publishedAt":18},"01M786DH4Y3AD551JN35D2R3HY","ts-code-review","https:\u002F\u002Fgithub.com\u002Fallanpablo\u002Fforja\u002Fblob\u002Fmain\u002Ftemplates\u002Fproject\u002Fskills\u002Fts-code-review\u002FSKILL.md",[],[],{"skillNo":73,"name":74,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":77,"publishedAt":18},"01M786DDCF8GHQ8KY2X4DGY3XN","threat-modeling","https:\u002F\u002Fgithub.com\u002Fallanpablo\u002Fforja\u002Fblob\u002Fmain\u002Ftemplates\u002Fproject\u002Fskills\u002Fthreat-modeling\u002FSKILL.md",[],[],{"skillNo":79,"name":80,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":81,"categories":82,"keywords":83,"publishedAt":18},"01M786DBDE38J5471FBVFB1DV8","testing","https:\u002F\u002Fgithub.com\u002Fallanpablo\u002Fforja\u002Fblob\u002Fmain\u002Ftemplates\u002Fproject\u002Fskills\u002Ftesting\u002FSKILL.md",[],[]]