O Async/Await permite lidar com requisições assíncronas e promessas no JavaScript. Aprenda técnicas avançadas para otimizar o código. 

A habilidade de lidar com requisições assíncronas em JavaScript é fundamental para a construção de aplicações robustas e eficientes.  

Neste guia, mergulharemos no universo de promises (promessas) e Async/Await, mostrando como aplicar esses conceitos para otimizar o código e aprimorar a experiência do usuário. 

Exploraremos desde os fundamentos das promessas até técnicas avançadas de composição e gestão de erros, proporcionando um conhecimento prático para aumentar o seu domínio sobre o desenvolvimento assíncrono em JavaScript. 

Continue a leitura para saber como aproveitar ao máximo as promises e Async/Await para criar aplicações não bloqueantes! 

Navegue pelo índice

    O que são promessas no JavaScript? 

    As promessas no JavaScript são objetos que representam o resultado de uma operação assíncrona que ainda não foi concluída, mas que eventualmente resultará em um valor ou em um erro. 

    Elas são classificadas em três estados: 

    1. Pendente: estado inicial da promessa, em que a operação está em andamento e seu resultado ainda não está disponível. 
    1. Resolvida (fulfilled): a operação foi concluída com sucesso e o valor esperado foi retornado. 
    1. Rejeitada (rejected): a operação assíncrona falhou e um erro foi retornado. 

    As promessas registram funções de retorno de chamada para serem executadas quando a promise for resolvida ou rejeitada, utilizando os métodos `.then()` e `.catch()`, respectivamente.  

    Este método é especialmente útil em situações práticas, como requisições de rede, leitura de arquivos e processamento de grandes volumes de dados, em que a eficiência e a não obstrução da execução são cruciais. 

    As promises permitem escrever código que espera um resultado sem bloquear a execução de outras operações, o que é importante para manter a fluidez de aplicações web. 

    Como criar e usar promessas para requisições assíncronas? 

    As promessas são úteis para lidar com operações assíncronas de forma mais clara e organizada em JavaScript.  

    Vamos demonstrar como criar e utilizar promises para realizar, por exemplo, uma chamada a uma API

    function fetchData() { 

        return new Promise((resolve, reject) => { 

            setTimeout(() => { 

                const data = { id: 1, name: ‘John’ }; 

                resolve(data); 

            }, 2000); 

        }); 

    } 

    fetchData() 

        .then(data => { 

            console.log(‘Dados obtidos:’, data); 

        }) 

        .catch(error => { 

            console.error(‘Erro ao obter dados:’, error); 

        }); 

    No exemplo, a função `fetchData()` retorna uma promessa que simula uma requisição assíncrona por meio de um atraso de dois segundos. 

    Em seguida, utilizamos os métodos `.then()` e `.catch()` para lidar com o resultado da promise, imprimindo os dados obtidos no console, em caso de sucesso, ou exibindo um erro, se houver falha. 

    E o que são funções Async/Await? 

    As funções Async/Await são uma maneira de escrever código assíncrono em JavaScript de uma forma mais lógica e legível

    Elas são construídas sobre o conceito de promessas e oferecem uma sintaxe limpa e intuitiva para lidar com operações assíncronas. 

    A palavra-chave ´async´ é usada antes de uma função para indicar que ela retornará uma promessa. 

    Em uma função async, você pode usar a palavra-chave ´await´ para pausar a execução e esperar que uma promise seja resolvida antes de continuar. 

    Isso permite escrever código assíncrono de uma maneira que pareça síncrona e sequencial, evitando a pirâmide de callbacks (callback hell) associada a operações assíncronas. 

    Assim, podemos encadear chamadas de função assíncrona de forma mais natural, sem a necessidade de encadeamento complexo de promessas usando `.then()`. 

    O código assíncrono com Async/Await pode ser facilmente integrado às estruturas de controle como loops `for` e condicionais `if`, tornando a lógica de programação mais direta. 

    Como encadear promessas de forma eficiente? 

    Encadear promessas torna o código mais legível, especialmente em projetos grandes e complexos, em que a clareza é essencial para a manutenção e colaboração. 

    Quando as promises são encadeadas corretamente, é mais simples adicionar novas funcionalidades, corrigir bugs e fazer alterações. 

    Confira as principais estratégias de encadeamento de promessas. 

    Encadeamento sequencial 

    Uma estratégia eficiente é encadear promessas sequencialmente, em que cada uma depende do resultado da anterior

    Isso é possível empregando a palavra-chave `await` dentro de uma função assíncrona ou encadeando múltiplas chamadas usando `.then()`, como abaixo: 

       async function fetchAndProcessData() { 

           const data1 = await fetchData1(); 

           const processedData1 = await processData(data1); 

           const data2 = await fetchData2(processedData1); 

           const processedData2 = await processData(data2); 

           return processedData2; 

       } 

    Encadeamento com `then()` 

    Além do `await`, as promessas também podem ser encadeadas usando o método `.then()`

    Isso é útil quando se deseja realizar várias operações em uma promise resolvida sem esperar que todas as anteriores sejam concluídas

    Confira um exemplo de encadeamento com `.then()`: 

       fetchData1() 

           .then(processData) 

           .then(fetchData2) 

           .then(processData) 

           .then(result => { 

               console.log(result); 

           }) 

           .catch(error => { 

               console.error(error); 

           }); 

    Encadeamento paralelo com promisse.all() 

    Se múltiplas tarefas assíncronas podem ser executadas independentemente umas das outras, é possível encadeá-las em paralelo usando `Promise.all()`.  

    Esse método aceita um array de promessas e retorna uma nova promessa, que é resolvida, quando todas as promises no array forem resolvidas, ou rejeitada, se uma delas for rejeitada. 

    Assim, todas as promises são retornadas simultaneamente

    Um exemplo: 

       async function fetchAndProcessData() { 

           const [data1, data2] = await Promise.all([fetchData1(), fetchData2()]); 

           const processedData1 = processData(data1); 

           const processedData2 = processData(data2); 

           return [processedData1, processedData2]; 

       } 

    Encadeamento com Promise.race() 

    O método `Promise.race()` também aceita um array de promessas, mas retorna uma nova promessa, que é resolvida ou rejeitada assim que uma das promises no array for resolvida ou rejeitada.  

    Exemplo de uso de `Promise.race()`: 

       const promises = [fetchData1(), fetchData2(), fetchData3()]; 

       Promise.race(promises) 

           .then(result => { 

               console.log(result); 

           }) 

           .catch(error => { 

               console.error(error); 

           }); 

    O método `Promise.race()` é útil em cenários onde a resposta mais rápida é a mais importante.  

    Por exemplo, em uma aplicação que precisa exibir um conteúdo rapidamente, podemos usar `Promise.race()` para esperar pela resposta do servidor que chegar primeiro, ignorando as outras. 

    A abordagem é ideal para situações como carregamentos de imagens em uma galeria, em que queremos mostrar a primeira imagem disponível o mais rápido possível. 

    Como gerenciar erros em cadeias assíncronas de operações com JavaScript? 

    Na gestão de erros em cadeias assíncronas, é essencial adotar estratégias para lidar com possíveis falhas que possam ocorrer durante a execução de promessas ou funções usando Async/Await. 

    Descubra quais são as melhores práticas para gerenciar erros de forma eficaz. 

    Tratamento de erros com try-catch 

    O uso de blocos try-catch é uma maneira eficaz de capturar exceções e lidar com erros de forma controlada. 

    Ao envolver o código assíncrono em um bloco try-catch, podemos capturar qualquer erro que ocorra durante a execução e fornecer um tratamento adequado para ele. 

    Isso pode incluir a exibição de mensagens para a pessoa usuária, registrando os erros em logs ou executando ações alternativas para mitigar o problema. 

    Confira um caso de uso: 

    async function fetchData() { 

        try { 

            const response = await fetch(‘https://api.example.com/data’); 

            const data = await response.json(); 

            return data; 

        } catch (error) { 

            console.error(‘Ocorreu um erro:’, error); 

            throw new Error(‘Falha ao buscar os dados.’); 

        } 

    } 

    fetchData() 

        .then(data => console.log(‘Dados recebidos:’, data)) 

        .catch(error => console.error(‘Erro ao processar os dados:’, error)); 

    Encadeamento de tratamento de erros 

    Ao encadear promessas ou funções assíncronas, é importante garantir que os erros sejam tratados em cada etapa do encadeamento

    Isso pode ser feito adicionando blocos try-catch em cada função assíncrona ou usando métodos como `catch()` para capturar erros em promises encadeadas, conforme abaixo: 

    async function fetchData() { 

        try { 

            const response = await fetch(‘https://api.example.com/data’); 

            const data = await response.json(); 

            return data; 

        } catch (error) { 

            console.error(‘Erro ao buscar os dados:’, error); 

            throw new Error(‘Falha ao buscar os dados.’); 

        } 

    } 

    async function processData() { 

        try { 

            const data = await fetchData(); 

            // Processar os dados recebidos 

            return processedData; 

        } catch (error) { 

            console.error(‘Erro ao processar os dados:’, error); 

            throw new Error(‘Falha ao processar os dados.’); 

        } 

    } 

    processData() 

        .then(result => console.log(‘Resultado final:’, result)) 

        .catch(error => console.error(‘Erro ao executar o processo:’, error)); 

    Gestão de erros assíncronos 

    Em situações em que os erros podem ocorrer de forma assíncrona em paralelo, como ao utilizar `Promise.all()`, é importante garantir que todos os erros sejam capturados e tratados adequadamente

    Isso é possível usando métodos como `Promise.allSettled()` para aguardar o término de todas as promessas, independentemente de terem sido resolvidas ou rejeitadas, e depois verificar o status de cada uma para lidar com os erros. 

    Um exemplo: 

    Promise.allSettled(promises) 

        .then(results => { 

            results.forEach(result => { 

                if (result.status === ‘rejected’) { 

                    console.error(‘Erro:’, result.reason); 

                } 

            }); 

        }) 

        .catch(error => console.error(‘Erro ao lidar com erros:’, error)); 

    Leia mais

    Como otimizar o desempenho com Async/Await?

    Mulher asiática com blusa amarela e branca parece explicar algo a seu gerente enquanto gesticula. Ele também é asiático, veste uma camisa azul. Olha para a tela de um desktop e parece sério.

    Otimizar o código assíncrono reduz o tempo de espera e o consumo de recursos, resultando em uma execução mais rápida e eficiente das operações. 

    Ao aplicar essas estratégias de otimização de desempenho com Async/Await e promessas em JavaScript, podemos criar aplicações mais rápidas e eficientes, proporcionando uma melhor experiência às pessoas usuárias finais. 

    Confira as principais estratégias de otimização com Async/Await em JavaScript: 

    • Utilizar apenas quando necessário: evite usar Async/Await em operações síncronas simples, que podem ser tratadas de forma mais eficiente com métodos síncronos. 
    • Agrupar operações assíncronas: quando possível, agrupe várias operações assíncronas em uma única função para reduzir o número de chamadas e minimizar o tempo de espera. 
    • Utilizar Promise.all() para operações independentes: quando tiver várias operações assíncronas independentes, utilize Promise.all() para executá-las simultaneamente e aguardar que todas as promessas sejam resolvidas de maneira eficiente. 
    • Gerenciar exceções adequadamente: certifique-se de lidar com erros de forma eficaz usando blocos try-catch para capturar exceções e tratar erros apropriadamente, garantindo que o código não pare de executar em caso de falha. 
    • Evitar bloqueios desnecessários: evite bloquear a thread principal do JavaScript com operações assíncronas longas. Se possível, divida tarefas demoradas em partes menores e execute-as de maneira assíncrona em segundo plano, liberando a thread principal para outras operações. 

    Onde aplicar Async/Await? 

    No desenvolvimento de aplicações, lidar com operações assíncronas é uma necessidade comum.  

    Ao permitir escrever o código assíncrono com Async/Await é oferecida uma abordagem mais intuitiva e legível, facilitando a manutenção. 

    Seja ao fazer solicitações de rede, seja ao acessar bancos de dados ou executar tarefas de longa duração, a capacidade de gerenciar essas operações é fundamental para o desempenho e a experiência do usuário

    Conheça alguns casos de uso das funções. 

    Requisições de API 

    É possível usar o Async/Await para fazer chamadas HTTP para recuperar dados de um servidor em requisições de API. 

    Isso pode incluir a recuperação de um banco de dados remoto, integração com serviços de terceiros ou obtenção de informações dinâmicas para atualizar a interface do usuário. 

    Operações de E/S intensiva 

    As funções Async/Await são úteis para operações de entrada/saída intensivas, como leitura/gravação de arquivos, processamento de imagens ou acesso a bancos de dados

    Essas operações geralmente são assíncronas e podem bloquear a thread principal, se forem executadas de forma síncrona. 

    Autenticação e autorização  

    Ao lidar com autenticação de usuário, autorização e gerenciamento de sessões, é comum fazer chamadas assíncronas para verificar credenciais, validar tokens ou recuperar informações de perfil do usuário.  

    As funções Async/Await simplificam a lógica de autenticação e autorização, garantindo que essas operações sejam executadas de forma eficiente. 

    Manipulação de eventos e interface do usuário 

    Em aplicações web e móveis, Async/Await pode ser usado para lidar com eventos assíncronos, como cliques de botões, envios de formulários ou atualizações de estado da interface do usuário. 

    Assim, é possível criar interações dinâmicas, mas sem bloquear a interface do usuário durante operações assíncronas. 

    Operações em segundo plano 

    As funções possibilitam executar tarefas assíncronas em paralelo à interação da pessoa usuária, melhorando a eficiência e a capacidade de resposta da aplicação

    Sendo assim, Async/Await pode realizar operações em segundo plano em aplicações web, como processamento de dados em lotes, atualizações automáticas ou sincronização de dados.  

    Quais práticas adotar para garantir um código assíncrono limpo e eficiente? 

    Ao aplicar boas práticas, as pessoas desenvolvedoras aproveitam ao máximo o Async/Await em suas aplicações, criando código limpo e fácil de manter

    Confira as melhores práticas para garantir um código assíncrono eficiente. 

    • Gerenciamento de erros: sempre utilize blocos try-catch para capturar e lidar com erros em operações assíncronas para garantir que a aplicação possa se recuperar de falhas adequadamente e fornecer feedback útil à pessoa usuária. 
    • Desacoplamento e modularidade: separe a lógica assíncrona em funções reutilizáveis e modularize seu código para promover a coesão e o baixo acoplamento entre os componentes. Isso facilita a manutenção e a escalabilidade ao longo do tempo. 
    • Uso responsável de await: evite o uso excessivo de await em cadeias longas de promessas, pois isso pode levar a bloqueios e atrasos na execução do código. Em vez disso, considere usar métodos como Promise.all() para executar várias operações assíncronas em paralelo. 
    • Testes unitários e de integração: certifique-se de testar adequadamente o código assíncrono usando estruturas de teste como Jest, Mocha ou Jasmine, para que ele funcione conforme o esperado em diferentes cenários e condições de uso. 

    Como lidar com o uso intensivo de requisições assíncronas? 

    Com um servidor VPS Locaweb, você garante flexibilidade e autonomia para suas aplicações baseadas em JavaScript, aproveitando todos os recursos de um servidor físico, só que virtualizados na nuvem. 

    Impulsione o desempenho das suas aplicações JavaScript com o servidor VPS da Locaweb.

    Clique aqui!
    O autor

    Equipe Locaweb

    Viramos referência quando o assunto é internet, porque somos motivados pelo que fazemos: desenvolver. Nossa riqueza humana, cerca de mil funcionários, engrandece nosso portfólio de serviços de internet. Existimos para facilitar o desenvolvimento de pequenos e médios empreendedores que buscam o sucesso de seus negócios por meio desses serviços.

    Veja outros conteúdos desse autor