Programação Assíncrona em TypeScript: Guia Completo para Iniciantes
\n\nProgramação assíncrona é um dos conceitos-chave no desenvolvimento web moderno. Se você está começando a aprender TypeScript, entender como trabalhar com código assíncrono abrirá portas para criar aplicações rápidas e responsivas. Neste guia, abordaremos os fundamentos da programação assíncrona em TypeScript: desde callbacks simples até o moderno async/await, com foco na tipagem estrita que o TypeScript oferece.
O que é programação assíncrona e por que ela é necessária?
\n\nNa programação síncrona, o código é executado sequencialmente: cada operação seguinte espera a conclusão da anterior. Isso pode causar "congelamento" da interface se, por exemplo, for necessário carregar dados do servidor. A programação assíncrona permite executar operações longas (requisições a APIs, leitura de arquivos, temporizadores) em segundo plano, sem bloquear o fluxo principal de execução. O TypeScript, sendo uma extensão do JavaScript, suporta totalmente padrões assíncronos e adiciona a eles um poderoso sistema de tipos.
\n\nPromise: a base da assincronicidade em TypeScript
\n\nPromise é um objeto que representa o resultado de uma operação assíncrona, que pode estar em um de três estados: pending (pendente), fulfilled (concluída com sucesso) ou rejected (concluída com erro). No TypeScript, podemos especificar o tipo de dados que a promise retornará, tornando o código mais previsível.
Exemplo de criação de Promise com tipagem:
\n\n// Função que retorna uma Promise do tipo string\nfunction fetchUserData(userId: number): Promise<string> {\n return new Promise((resolve, reject) => {\n setTimeout(() => {\n if (userId > 0) {\n resolve(`Dados do usuário com ID ${userId}`);\n } else {\n reject(new Error("ID de usuário inválido"));\n }\n }, 1000);\n });\n}\n\n// Uso da Promise\nfetchUserData(123)\n .then((data: string) => {\n console.log(data); // "Dados do usuário com ID 123"\n })\n .catch((error: Error) => {\n console.error(error.message);\n });\n\nObserve: especificamos explicitamente que a promise retorna string, e no .then() o parâmetro data também tem o tipo string. Isso ajuda a evitar erros em tempo de compilação.
Cadeias de Promise e tratamento de erros
\n\nPromises podem ser encadeadas, passando o resultado de uma operação assíncrona para outra. O TypeScript inferirá automaticamente os tipos em cada etapa.
\n\nExemplo de cadeia de Promise:
\n\ninterface User {\n id: number;\n name: string;\n}\n\nfunction getUser(id: number): Promise<User> {\n return new Promise((resolve) => {\n setTimeout(() => resolve({ id, name: "Ana" }), 500);\n });\n}\n\nfunction getPosts(userId: number): Promise<string[]> {\n return new Promise((resolve) => {\n setTimeout(() => resolve(["Post 1", "Post 2"]), 500);\n });\n}\n\n// Cadeia de chamadas\ngetUser(1)\n .then((user: User) => {\n console.log(`Usuário: ${user.name}`);\n return getPosts(user.id); // Retorna uma nova Promise\n })\n .then((posts: string[]) => {\n console.log(`Posts: ${posts.join(", ")}`);\n })\n .catch((error: Error) => {\n console.error("Erro:", error.message);\n });\n\nSe ocorrer um erro em qualquer etapa, a execução passará para o .catch() mais próximo. Este tratamento centralizado de erros é uma das principais vantagens das promises sobre callbacks.
Async/await: sintaxe elegante para assincronicidade
\n\nA sintaxe async/await torna o código assíncrono semelhante ao síncrono, facilitando sua leitura