Tutorial

Como escrever um código assíncrono em Node.js

Published on March 19, 2020
Português
Como escrever um código assíncrono em Node.js

O autor selecionou a Open Internet/Free Speech Fund para receber uma doação como parte do programa Write for DOnations.

Introdução

Com muitos programas em JavaScript, o código é executado da maneira como o programador o escreveu — linha por linha. A isto chamamos de execução síncrona, pois as linhas são executadas uma após a outra, na ordem em que foram escritas. No entanto, nem toda instrução que você dá ao computador precisa ser atendida imediatamente. Por exemplo, caso envie uma solicitação de rede, o processo que executa seu código terá que esperar pelo retorno dos dados, antes de poder trabalhar neles. Nesse caso, seria um desperdício de tempo se o computador não executasse outro código enquanto aguarda a solicitação da rede ser concluída. Para resolver esse problema, os desenvolvedores usam a programação assíncrona, na qual as linhas do código são executadas em uma ordem diferente daquela em que foram escritas. Com a programação assíncrona, podemos executar outros códigos enquanto esperamos por atividades longas terminarem, como os pedidos de rede.

O código JavaScript é executado em um único thread dentro de um processo de computador. Seu código é processado de maneira sincronizada nesse thread, com apenas uma instrução sendo executada por vez. Portanto, caso fôssemos realizar uma tarefa de execução demorada nesse thread, todo o código restante ficaria bloqueado até que a tarefa estivesse concluída. Para evitar esse problema, podemos aproveitar-nos dos recursos de programação assíncrona do JavaScript, descarregando tarefas de execução demorada em um thread em segundo plano. Quando a tarefa é concluída, o código que precisamos para processar os dados da tarefa é colocado de volta no thread único principal.

Neste tutorial, você aprenderá como o JavaScript gerencia tarefas assíncronas com a ajuda do Loop de eventos, que consiste em um constructo do JavaScript que conclui uma nova tarefa enquanto aguarda por outra. Depois, criará um programa que utilize a programação assíncrona para solicitar uma lista de filmes de uma API do Studio Ghibli e salvará os dados em um arquivo CSV. O código assíncrono será escrito de três maneiras: callbacks (retornos de chamadas), promises (promessas) e com as palavras-chave async/await.

Nota: no momento desta publicação, a programação assíncrona não é mais feita usando apenas com os callbacks. Entretanto, aprender esse método obsoleto pode proporcionar um ótimo contexto quanto ao motivo pelo qual a comunidade JavaScript agora usa as promises. As palavras-chave async/await nos permitem usar as promises de uma maneira menos detalhada. Assim, no momento em que este artigo foi escrito, essa era a maneira padrão de se fazer a programação assíncrona em JavaScript.

Pré-requisitos

O loop de eventos

Vamos começar estudando o funcionamento interno da execução de funções em JavaScript. Entender como as funções se comportam permitirá que você escreva um código assíncrono de maneira mais consciente, bem como ajudará você a solucionar problemas do código no futuro.

À medida que o programa interpretador de JavaScript executa o código, cada função que é chamada é adicionada à pilha de chamadas do JavaScript. A pilha de chamadas é uma* pilha* — uma estrutura de dados do tipo lista, na qual os itens apenas podem ser adicionados ao topo e removidos do topo. As pilhas seguem o princípio “Último a entrar, primeiro a sair”, ou princípio LIFO (do inglês “Last in, first out”). Se adicionar dois itens na pilha, o item adicionado por último será removido primeiro.

Vamos ilustrar com um exemplo usando a pilha de chamadas. Se o JavaScript encontra uma função functionA() sendo chamada, ela é adicionada à pilha de chamadas. Se essa função, functionA() chama outra função, functionB(), então a functionB() é adicionada ao topo da pilha de chamadas. À medida que o JavaScript completa a execução de uma função, ela é removida da pilha de chamadas. Portanto, o JavaScript executará a functionB() primeiro, irá removê-la da pilha quando terminar para, então, terminar a execução da functionA() e remover esta da pilha de chamadas. É por isso que as funções internas são sempre executadas antes de suas funções externas.

Quando o JavaScript encontra uma operação assíncrona, como gravar algo em um arquivo, ele adiciona essa operação a uma tabela de sua memória. Essa tabela armazena a operação, a condição para que ela seja completada e a função a ser chamada quando ela for concluída. Quando a operação é concluída, o JavaScript adiciona a função associada à fila de mensagens. Uma fila é outra estrutura de dados do tipo lista. Nela, os itens apenas podem ser adicionados ao final, mas removidos do topo. Na fila de mensagens, se duas ou mais operações assíncronas estiverem prontas para que suas funções sejam executadas, a operação assíncrona que foi concluída primeiro terá sua função marcada para executar primeiro.

As funções na fila de mensagens estão esperando para serem adicionadas à pilha de chamadas. O loop de eventos é um processo contínuo que verifica se a pilha de chamadas está vazia. Se estiver, então o primeiro item na fila de mensagens é movido para a pilha de chamadas. O JavaScript prioriza as funções na fila de mensagens, em detrimento das chamadas de função que ele interpreta no código. O efeito combinado da pilha de chamadas, da fila de mensagens e do loop de eventos permite que código em JavaScript seja processado enquanto gerencia as atividades assíncronas.

Agora que possui uma comprensão abrangente sobre o loop de eventos, você sabe como o código assíncrono que você escrever será executado. Com esse conhecimento, agora você pode criar um código assíncrono com três abordagens diferentes: callbacks, promises e async/await.

Programação assíncrona com callbacks

Uma função callback é aquela que é enviada para outra função como um argumento e, em seguida, é executada - quando a outra função tiver sido finalizada. Usamos as callbacks para garantir que o código somente será executado depois da conclusão de uma operação assíncrona.

Por muito tempo, os callbacks foram o mecanismo mais comum para se escrever códigos assíncronos. Agora, porém, eles se tornaram, em grande parte, obsoletos, uma vez que podem tornar um código confuso para ler. Neste passo, você escreverá um exemplo de código assíncrono usando callbacks, de modo que você possa usá-lo como um parâmetro de comparação para perceber a eficiência das demais estratégias.

Existem muitas maneiras de se usar as funções de callback em outra função. Geralmente, elas adotam esta estrutura:

function asynchronousFunction([ Function Arguments ], [ Callback Function ]) {
    [ Action ]
}

Embora nem o JavaScript nem o Node.js tenham a exigência sintática de trazer a função de callback como o último argumento da função externa, trata-se de uma prática comum para facilitar a identificação dos callbacks. Também é comum para os desenvolvedores em JavaScript usar uma função anônima como um callback. As funções anônimas são aquelas criadas sem um nome. Normalmente, uma função fica muito mais legível quando definida no final da lista de argumentos.

Para demonstrar os callbacks, vamos criar um módulo do Node.js que grava uma lista dos filmes do Studio Ghibli em um arquivo. Primeiro, crie uma pasta que armazenará nosso arquivo JavaScript e seu resultado:

  1. mkdir ghibliMovies

Então, acesse aquela pasta:

  1. cd ghibliMovies

Começaremos fazendo uma solicitação HTTP à API do Studio Ghibli, da qual nossa função de callback registrará os resultados. Para tanto, instalaremos uma biblioteca que nos permitirá acessar os dados de uma resposta do HTTP em um callback.

No seu terminal, inicialize o npm para termos uma referência para nossos pacotes mais tarde:

  1. npm init -y

Em seguida, instale a biblioteca request (solicitação):

  1. npm i request --save

Agora, abra um novo arquivo chamado callbackMovies.js em um editor de texto como o nano:

  1. nano callbackMovies.js

No seu editor de texto, digite o código a seguir. Vamos começar enviando uma solicitação HTTP com o módulo request:

callbackMovies.js
const request = require('request');

request('https://ghibliapi.herokuapp.com/films');

Na primeira linha, carregaremos o módulo request - o qual foi instalado via npm [NT: gerenciador de pacotes para a linguagem de programação JavaScript]. O módulo retorna uma função que pode fazer pedidos via HTTP; então, salvamos essa função na constante request.

Depois, realizamos a solicitação HTTP usando a função request(). Agora, vamos imprimir os dados da solicitação HTTP no console, adicionando as alterações destacadas:

callbackMovies.js
const request = require('request');

request('https://ghibliapi.herokuapp.com/films', (error, response, body) => {
    if (error) {
        console.error(`Could not send request to API: ${error.message}`);
        return;
    }

    if (response.statusCode != 200) {
        console.error(`Expected status code 200 but received ${response.statusCode}.`);
        return;
    }

    console.log('Processing our list of movies');
    movies = JSON.parse(body);
    movies.forEach(movie => {
        console.log(`${movie['title']}, ${movie['release_date']}`);
    });
});

Quando usamos a função request(), damos a ela dois parâmetros:

  • O URL do site que estamos tentando solicitar
  • Uma função de callback que lide com erros ou respostas bem-sucedidas depois que a solicitação for concluída

Nossa função de callback tem três argumentos: error (erro), response (reposta) e body (corpo). Quando a solicitação HTTP é concluída, os argumentos recebem valores automaticamente, dependendo do resultado. Caso houvesse falha no envio da solicitação, então o error conteria um objeto, mas o argumento response e body seriam null (nulo). Se o pedido fosse bem-sucedido, então a resposta HTTP seria armazenada em response. Se nossa resposta HTTP retornasse dados (neste exemplo, obtivemos JSON), então os dados seriam definidos em body.

Primeiro, nossa função de callback verifica se recebemos um erro. Verificar primeiro se há erros em um callback é uma boa prática. Dessa maneira, a execução do callback não continuará com dados faltando. Neste caso, registramos o erro e a execução da função. Em seguida, verificamos o código do status da resposta. Nem sempre nosso servidor pode estar disponível e as APIs podem sofrer alterações, fazendo com que solicitações, antes adequadas, tornem-se incorretas. Ao verificar se o código de status é 200 - que significa que o pedido estava “OK”, podemos confiar que nossa resposta será a que esperamos que seja.

Por fim, analisamos o corpo da resposta em uma Array (matriz) e executamos um loop em cada filme para registrar seu nome e ano de lançamento.

Após salvar e sair do arquivo, execute este script com:

  1. node callbackMovies.js

Você receberá o seguinte resultado:

Output
Castle in the Sky, 1986 Grave of the Fireflies, 1988 My Neighbor Totoro, 1988 Kiki's Delivery Service, 1989 Only Yesterday, 1991 Porco Rosso, 1992 Pom Poko, 1994 Whisper of the Heart, 1995 Princess Mononoke, 1997 My Neighbors the Yamadas, 1999 Spirited Away, 2001 The Cat Returns, 2002 Howl's Moving Castle, 2004 Tales from Earthsea, 2006 Ponyo, 2008 Arrietty, 2010 From Up on Poppy Hill, 2011 The Wind Rises, 2013 The Tale of the Princess Kaguya, 2013 When Marnie Was There, 2014

Felizmente, recebemos uma lista dos filmes do Studio Ghibli, contendo o ano em que foram lançados. Agora, vamos completar esse programa, escrevendo a lista de filmes que estamos registrando em um arquivo no momento.

Atualize o arquivo callbackMovies.js em seu editor de texto, de modo a incluir o seguinte código destacado, o qual cria um arquivo CSV com os nossos dados dos filmes:

callbackMovies.js
const request = require('request');
const fs = require('fs');

request('https://ghibliapi.herokuapp.com/films', (error, response, body) => {
    if (error) {
        console.error(`Could not send request to API: ${error.message}`);
        return;
    }

    if (response.statusCode != 200) {
        console.error(`Expected status code 200 but received ${response.statusCode}.`);
        return;
    }

    console.log('Processing our list of movies');
    movies = JSON.parse(body);
    let movieList = '';
    movies.forEach(movie => {
        movieList += `${movie['title']}, ${movie['release_date']}\n`;
    });

    fs.writeFile('callbackMovies.csv', movieList, (error) => {
        if (error) {
            console.error(`Could not save the Ghibli movies to a file: ${error}`);
            return;
        }

        console.log('Saved our list of movies to callbackMovies.csv');;
    });
});

Ao notar as alterações destacadas, vemos que importamos o módulo fs. Esse módulo é padrão em todas as instalações do Node.js e contém um método writeFile() que pode escrever em um arquivo de maneira assíncrona.

Em vez de registrar os dados no console, agora nós vamos adicioná-los a uma variável de string - movieList. Então, usamos o writeFile() para salvar o conteúdo da movieList em um novo arquivo — callbackMovies.csv. Por fim, fornecemos um callback à função writeFile(), que tem um argumento: error. Isso nos permite lidar com casos em que não conseguimos escrever em um arquivo, como, por exemplo, quando o usuário em que estamos executando o processo node não tem essas permissões.

Salve o arquivo e execute esse programa Node.js novamente com:

  1. node callbackMovies.js

Em sua pasta ghibliMovies, você verá o callbackMovies.csv, que tem o seguinte conteúdo:

callbackMovies.csv
Castle in the Sky, 1986
Grave of the Fireflies, 1988
My Neighbor Totoro, 1988
Kiki's Delivery Service, 1989
Only Yesterday, 1991
Porco Rosso, 1992
Pom Poko, 1994
Whisper of the Heart, 1995
Princess Mononoke, 1997
My Neighbors the Yamadas, 1999
Spirited Away, 2001
The Cat Returns, 2002
Howl's Moving Castle, 2004
Tales from Earthsea, 2006
Ponyo, 2008
Arrietty, 2010
From Up on Poppy Hill, 2011
The Wind Rises, 2013
The Tale of the Princess Kaguya, 2013
When Marnie Was There, 2014

É importante notar que estamos escrevendo em nosso arquivo CSV no callback da solicitação HTTP. Assim que o código estiver na função de callback, ele apenas gravará no arquivo após a solicitação HTTP ser finalizada. Se quiséssemos nos comunicar com um banco de dados - depois de termos escrito nosso arquivo CSV, faríamos outra função assíncrona, a qual seria chamada no callback da função writeFile(). Quanto mais código assíncrono tivermos, mais funções de callback precisam ser aninhadas.

Imaginemos um cenário em que queiramos executar cinco operações assíncronas, cada uma podendo ser executada apenas quando outra for concluída. Se tivéssemos que codificar isso, teríamos algo parecido com o seguinte:

doSomething1(() => {
    doSomething2(() => {
        doSomething3(() => {
            doSomething4(() => {
                doSomething5(() => {
                    // final action
                });
            });
        });
    });
});

Quando os callbacks aninhados têm muitas linhas de código a serem executadas, eles se tornam consideravelmente mais complexos e ilegíveis. À medida que seu projeto JavaScript cresce em tamanho e complexidade, esse efeito se tornará mais visível, até que, no final das contas, ele se torna não gerenciável. Por isso, os desenvolvedores não usam mais callbacks para lidar com operações assíncronas. Para melhorar a sintaxe do nosso código assíncrono, podemos usar promises como alternativa.

Usando promises para obter uma programação assíncrona concisa

Uma promise é um objeto JavaScript que retornará um valor em algum momento no futuro. As funções assíncronas podem retornar objetos promise, em vez de valores concretos. Se recebermos um valor no futuro, dizemos que a promessa (promise) foi cumprida. Se recebermos um erro no futuro, dizemos que a promessa foi rejeitada. Caso contrário, a promessa ainda estará sendo trabalhada em um estado pendente.

As promises geralmente adotam a seguinte forma:

promiseFunction()
    .then([ Callback Function for Fulfilled Promise ])
    .catch([ Callback Function for Rejected Promise ])

Como mostrado nesse modelo, as promises também usam funções de callback. Temos uma função de callback para o método then(), que é executado quando uma promise é realizada. Também temos uma função de callback para o método catch() para lidar com todo erro que aparecer enquanto a promessa é executada.

Vamos ter uma experiência direta com as promises, reescrevendo nosso programa do Studio Ghibli para que use promises como alternativa.

O Axios é um cliente HTTP baseado em promises para JavaScript. Assim, vamos prosseguir e instalá-lo:

  1. npm i axios --save

Agora, com o editor de sua escolha, crie um novo arquivo promiseMovies.js:

  1. nano promiseMovies.js

Nosso programa fará uma solicitação HTTP com o axios e depois usará uma versão especial do fs - baseada em promise - para salvar em um novo arquivo CSV.

Digite este código em promiseMovies.js para que possamos carregar o Axios e enviar uma solicitação HTTP para a API de filmes:

promiseMovies.js
const axios = require('axios');

axios.get('https://ghibliapi.herokuapp.com/films');

Na primeira linha, carregamos o módulo axios, armazenando a função retornada em uma constante chamada axios. Em seguida, usamos o método axios.get() para enviar uma solicitação HTTP para a API.

O método axios.get() retorna uma promise. Vamos encadear aquela promise para que possamos imprimir a lista dos filmes Ghibli no console:

promiseMovies.js
const axios = require('axios');
const fs = require('fs').promises;


axios.get('https://ghibliapi.herokuapp.com/films')
    .then((response) => {
        console.log('Successfully retrieved our list of movies');
        response.data.forEach(movie => {
            console.log(`${movie['title']}, ${movie['release_date']}`);
        });
    })

Vamos detalhar o que está acontecendo. Após realizar uma solicitação HTTP GET com o axios.get(), usamos a função then(), que é executada apenas quando a promise é realizada. Neste caso, imprimimos os filmes na tela como fizemos no exemplo dos callbacks.

Para melhorar esse programa, adicione o código destacado para escrever os dados HTTP em um arquivo:

promiseMovies.js
const axios = require('axios');
const fs = require('fs').promises;


axios.get('https://ghibliapi.herokuapp.com/films')
    .then((response) => {
        console.log('Successfully retrieved our list of movies');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });

        return fs.writeFile('promiseMovies.csv', movieList);
    })
    .then(() => {
        console.log('Saved our list of movies to promiseMovies.csv');
    })

Além disso, importamos o módulo fs novamente. Note que, após a importação do fs, agora temos a extensão .promises. O Node.js inclui uma versão baseada em promises da biblioteca fs baseada em callbacks, de modo que a retrocompatibilidade não é quebrada em projetos herdados.

A primeira função then() que processa a solicitação HTTP agora chama o fs.writeFile(), em vez de imprimir no console. Como importamos a versão baseada em promises do fs, nossa função writeFile() retorna outra promise. Desta forma, anexamos outra função then() para quando a promise da writeFile() for realizada.

Uma promise pode retornar uma nova promise, permitindo que executemos promises uma após a outra. Isso nos possibilita realizar múltiplas operações assíncronas. Isso se chama encadeamento de promises, sendo comparáveis às callbacks aninhadas. O segundo then() é chamado apenas após gravarmos com sucesso no arquivo.

Nota: neste exemplo, não verificamos o código de status do HTTP como fizemos no exemplo das callbacks. Por padrão, o axios não realiza duas promises caso receber um código de status indicando um erro. Desta forma, não precisamos mais validá-lo.

Para completar esse programa, encadeie a promise com uma função catch(), como destacado a seguir:

promiseMovies.js
const axios = require('axios');
const fs = require('fs').promises;


axios.get('https://ghibliapi.herokuapp.com/films')
    .then((response) => {
        console.log('Successfully retrieved our list of movies');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });

        return fs.writeFile('promiseMovies.csv', movieList);
    })
    .then(() => {
        console.log('Saved our list of movies to promiseMovies.csv');
    })
    .catch((error) => {
        console.error(`Could not save the Ghibli movies to a file: ${error}`);
    });

Se alguma promise deixar de ser realizada na cadeia de promises, o JavaScript automaticamente vai para a função catch() - caso ela tenha sido definida. É por isso que temos apenas uma cláusula catch(), apesar de termos duas operações assíncronas.

Vamos confirmar se nosso programa produz o mesmo resultado, executando:

  1. node promiseMovies.js

Na sua pasta ghibliMovies, você verá o arquivo promiseMovies.csv, que contém:

promiseMovies.csv
Castle in the Sky, 1986
Grave of the Fireflies, 1988
My Neighbor Totoro, 1988
Kiki's Delivery Service, 1989
Only Yesterday, 1991
Porco Rosso, 1992
Pom Poko, 1994
Whisper of the Heart, 1995
Princess Mononoke, 1997
My Neighbors the Yamadas, 1999
Spirited Away, 2001
The Cat Returns, 2002
Howl's Moving Castle, 2004
Tales from Earthsea, 2006
Ponyo, 2008
Arrietty, 2010
From Up on Poppy Hill, 2011
The Wind Rises, 2013
The Tale of the Princess Kaguya, 2013
When Marnie Was There, 2014

Com as promises, podemos escrever um código muito mais conciso do que usando apenas callbacks. A cadeia de promises das callbacks é uma opção mais organizada do que o aninhamento de callbacks. No entanto, à medida que fazemos mais chamadas assíncronas, nossa cadeia de promises se torna mais longa e difícil de manter.

O detalhamento das funções callbacks e promises vem da necessidade de se criar funções quando temos o resultado de uma tarefa assíncrona. Uma prática melhor seria aguardar por um resultado assíncrono e colocá-lo em uma variável fora da função. Dessa maneira, podemos usar os resultados nas variáveis sem ter que criar uma função. Podemos conseguir isso com as palavras-chave async e await.

Escrevendo JavaScript com async/await

As palavras-chave async/await fornecem uma sintaxe alternativa ao trabalhar com promises. Em vez de ter o resultado de uma promise disponível no método then(), o resultado é retornado como um valor, assim como em qualquer outra função. Definimos uma função com a palavra-chave async para dizer ao JavaScript que é uma função assíncrona que retorna uma promise. Usamos a palavra-chave await para dizer ao JavaScript para retornar os resultados da promise, em vez de retornar a promise, propriamente dita, quanto ela for realizada.

De um modo geral, o uso de async/await fica com a seguinte aparência:

async function() {
    await [Asynchronous Action]
}

Vejamos de que maneira o uso de async/await pode melhorar nosso programa do Studio Ghibli. Use seu editor de texto para criar e abrir um novo arquivo asyncAwaitMovies.js:

  1. nano asyncAwaitMovies.js

No seu arquivo em JavaScript recém-criado, vamos começar importando os mesmos módulos que usamos em nosso exemplo de promise:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

As importações são as mesmas do promiseMovies.js, pois async/await usa promises.

Agora, usamos a palavra-chave async para criar uma função com nosso código assíncrono:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {}

Criamos uma nova função chamada saveMovies(), mas incluímos async no início de sua definição. Isso é importante, pois somente podemos usar a palavra-chave await em uma função assíncrona.

Utilize a palavra-chave await para fazer uma solicitação HTTP que recebe a lista de filmes da API do Ghibli:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    let response = await axios.get('https://ghibliapi.herokuapp.com/films');
    let movieList = '';
    response.data.forEach(movie => {
        movieList += `${movie['title']}, ${movie['release_date']}\n`;
    });
}

Em nossa função saveMovies(), fazemos uma solicitação HTTP com o axios.get(), como antes. Desta vez, não a encadearemos com uma função then(). Em vez disso, adicionamos await antes que ela seja chamada. Quando o JavaScript vê await, ele somente executará o código restante da função depois que a função axios.get() terminar a execução e definir a variável response. O outro código salva os dados dos filmes para que possamos gravar em um arquivo.

Vamos gravar os dados dos filmes em um arquivo:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    let response = await axios.get('https://ghibliapi.herokuapp.com/films');
    let movieList = '';
    response.data.forEach(movie => {
        movieList += `${movie['title']}, ${movie['release_date']}\n`;
    });
    await fs.writeFile('asyncAwaitMovies.csv', movieList);
}

Também usamos a palavra-chave await quando escrevemos no arquivo com fs.writeFile().

Para completar essa função, precisamos capturar os erros que nossas promises podem gerar. Vamos fazer isso, encapsulando nosso código em um bloco try/catch (tentar/caputar):

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    try {
        let response = await axios.get('https://ghibliapi.herokuapp.com/films');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });
        await fs.writeFile('asyncAwaitMovies.csv', movieList);
    } catch (error) {
        console.error(`Could not save the Ghibli movies to a file: ${error}`);
    }
}

Como as promises podem falhar, envolvemos nosso código assíncrono com uma cláusula try/catch. Isso captará quaisquer erros que forem gerados quando as operações de solicitação HTTP ou as operações de gravação de arquivos falharem.

Por fim, vamos chamar nossa função assíncrona saveMovies() para que ela seja executada quando executarmos o programa com o node.

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    try {
        let response = await axios.get('https://ghibliapi.herokuapp.com/films');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });
        await fs.writeFile('asyncAwaitMovies.csv', movieList);
    } catch (error) {
        console.error(`Could not save the Ghibli movies to a file: ${error}`);
    }
}

saveMovies();

De imediato, o programa fica parecido com um bloco de código JavaScript síncrono típico. Ele tem menos funções sendo passadas, fazendo-o parecer mais organizado. Esses pequenos ajustes tornam o código assíncrono com async/await mais fáceis de manter.

Teste essa iteração do nosso programa, digitando isto em seu terminal:

  1. node asyncAwaitMovies.js

Em sua pasta ghibliMovies, um novo arquivo asyncAwaitMovies.csv será criado com o seguinte conteúdo:

asyncAwaitMovies.csv
Castle in the Sky, 1986
Grave of the Fireflies, 1988
My Neighbor Totoro, 1988
Kiki's Delivery Service, 1989
Only Yesterday, 1991
Porco Rosso, 1992
Pom Poko, 1994
Whisper of the Heart, 1995
Princess Mononoke, 1997
My Neighbors the Yamadas, 1999
Spirited Away, 2001
The Cat Returns, 2002
Howl's Moving Castle, 2004
Tales from Earthsea, 2006
Ponyo, 2008
Arrietty, 2010
From Up on Poppy Hill, 2011
The Wind Rises, 2013
The Tale of the Princess Kaguya, 2013
When Marnie Was There, 2014

Agora, você usou os recursos async/await do JavaScript para gerenciar um código assíncrono.

Conclusão

Neste tutorial, você aprendeu como o JavaScript lida com a execução de funções e o gerenciamento de operações assíncronas com o loop de eventos. Na sequência, você escreveu programas que criaram um arquivo CSV depois de fazer uma solicitação HTTP por dados de filmes, usando várias técnicas de programação assíncrona. Primeiro, você usou a abordagem obsoleta baseada em callbacks. Então, usou promises e, por fim, async/await para tornar a sintaxe da promise mais sucinta.

Com o conhecimento sobre códigos assíncronos com o Node.js, agora você pode desenvolver programas que se beneficiem da programação assíncrona, tais como aqueles que dependem de chamadas da API. Veja esta lista de APIs públicas. Para usá-las, você terá que fazer solicitações HTTP assíncronas, como fizemos neste tutorial. Para avançar nesse estudo, tente compilar um app que utilize essas APIs para praticar as técnicas que aprendeu aqui.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors

Default avatar

Senior Technical Editor

Editor at DigitalOcean, fiction writer and podcaster elsewhere, always searching for the next good nautical pun!


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel