Pular para o conteúdo principal

Escreva código assíncrono

Aprenda sobre programação assíncrona em Dart e como fazer requisições HTTP.

Neste capítulo, você vai explorar a programação assíncrona em Dart, que permite que seus aplicativos executem várias tarefas de forma concorrente. Você vai aprender a buscar dados da internet usando o pacote http, para obter um resumo de artigo da Wikipedia.

O que você vai realizar

Escreva código assíncrono com futures e await
Instale e use pacotes externos
Busque dados em tempo real da API da Wikipedia

Pré-requisitos

#
  • Ter concluído o Capítulo 2, que abordou a sintaxe básica do Dart e a interação pela linha de comando. Você deve ter um projeto dartpedia configurado.
  • Estar familiarizado com o conceito de APIs (Interfaces de Programação de Aplicações) como uma forma de obter dados.

Tarefas

#

Neste capítulo, você vai modificar o aplicativo CLI dartpedia existente para buscar e exibir um resumo de artigo usando o pacote http e técnicas de programação assíncrona.

Tarefa 1: Adicione a dependência http

#

Antes de fazer requisições HTTP, você precisa adicionar o pacote http como uma dependência do seu projeto.

  1. Abra o arquivo dartpedia/cli/pubspec.yaml dentro do seu projeto. Esse arquivo se chama pubspec e gerencia os metadados do seu projeto Dart, suas dependências (como o pacote http) e seus assets.

  2. Localize a seção dependencies.

  3. Adicione http: ^1.4.0 (ou a versão estável mais recente) em dependencies. O símbolo ^ permite que versões compatíveis sejam usadas.

    cli/pubspec.yaml
    yaml
    dependencies:
      http: ^1.4.0
    
  4. Salve o arquivo pubspec.yaml.

  5. Execute dart pub get no seu terminal, a partir do diretório dartpedia/cli. Esse comando baixa a dependência recém-adicionada e a deixa disponível para uso no seu projeto.

    Você deve ver uma saída parecida com esta:

    bash
    Resolving dependencies...
    Downloading packages...
    + http 1.4.0
      lints 5.1.1 (6.0.0 available)
    Changed 1 dependency!
    1 package has newer versions incompatible with dependency constraints.
    Try `dart pub outdated` for more information.
    

Tarefa 2: Importe o pacote http

#

Agora que você adicionou o pacote http, precisa importá-lo no seu arquivo Dart para usar suas funcionalidades.

  1. Abra o arquivo cli/bin/cli.dart.

  2. Adicione a seguinte instrução import no topo do arquivo, junto com o import existente de dart:io:

    cli/bin/cli.dart
    dart
    import 'dart:io';
    import 'package:http/http.dart' as http; // Add this line
    

    Essa linha importa o pacote http e dá a ele o alias http. Depois disso, você pode se referir a classes e funções do pacote http usando http. (por exemplo, http.Client, http.get). A parte as http é uma convenção comum para evitar conflitos de nome caso outra biblioteca importada também tenha uma classe ou função com nome semelhante.

Tarefa 3: Implemente a função getWikipediaArticle

#

Agora, crie uma nova função chamada getWikipediaArticle que cuida de buscar dados de uma API externa. Essa função será async, porque requisições de rede são operações assíncronas.

  1. Defina a assinatura da função: Abaixo da sua função main (e da função printUsage), adicione a seguinte assinatura de função.

    cli/bin/cli.dart
    dart
    // ... (your existing printUsage() function)
    ​
    Future<String> getWikipediaArticle(String articleTitle) async {
      //You'll add more code here soon
    }
    

    Destaques do código anterior:

    • O tipo de retorno Future<String> indica que essa função vai, eventualmente, produzir um resultado String, mas não imediatamente, porque é uma operação assíncrona.
    • A keyword async marca a função como assíncrona, permitindo que você use await dentro dela.
  2. Construa a URL da API: Dentro da sua nova função getWikipediaArticle, crie um objeto Uri. O Uri representa o endpoint da API da Wikipedia que você vai chamar para obter um resumo do artigo.

    Adicione estas linhas dentro da função getWikipediaArticle:

    cli/bin/cli.dart
    dart
    Future<String> getWikipediaArticle(String articleTitle) async {
      final url = Uri.https(
        'en.wikipedia.org', // Wikipedia API domain
        '/api/rest_v1/page/summary/$articleTitle', // API path for article summary
      );
      // ...
    }
    
  3. Faça a requisição HTTP e trate a resposta: Agora, use a função get de nível superior do package:http para fazer uma requisição HTTP GET para a URL que você acabou de construir. A keyword await pausa a execução de getWikipediaArticle até que a chamada get seja concluída e retorne um objeto http.Response.

    Depois que a requisição terminar, verifique o response.statusCode para garantir que ela foi bem-sucedida (um status code 200 significa OK). Se tiver sido bem-sucedida, retorne o response.body, que contém os dados obtidos (neste caso, JSON bruto). Se a requisição falhar, retorne uma mensagem de erro informativa.

    Adicione estas linhas depois da construção do Uri dentro de getWikipediaArticle:

    cli/bin/cli.dart
    dart
    Future<String> getWikipediaArticle(String articleTitle) async {
      final url = Uri.https(
        'en.wikipedia.org',
        '/api/rest_v1/page/summary/$articleTitle',
      );
      final response = await http.get(url); // Make the HTTP request
    ​
      if (response.statusCode == 200) {
        return response.body; // Return the response body if successful
      }
    ​
      // Return an error message if the request failed
      return 'Error: Failed to fetch article "$articleTitle". Status code: ${response.statusCode}';
    }
    

Tarefa 4: Integre a chamada da API em searchWikipedia

#

Você vai integrar a chamada da API em searchWikipedia. Essa função vai conter a lógica principal para tratar o comando wikipedia.

  1. Atualize searchWikipedia para usar async: Localize sua função searchWikipedia e atualize a assinatura para ser async, já que ela agora vai executar operações assíncronas.

    Sua função searchWikipedia deve ficar assim:

    cli/bin/cli.dart
    dart
    // ... (your existing main function)
    ​
    void searchWikipedia(List<String>? arguments) async { // Added 'async'
      final String articleTitle;
    ​
      // If the user didn't pass in arguments, request an article title.
      if (arguments == null || arguments.isEmpty) {
        print('Please provide an article title.');
        // Read input and provide a default empty string if the input is null.
        articleTitle = stdin.readLineSync() ?? '';
      } else {
        // Otherwise, join the arguments into a single string.
        articleTitle = arguments.join(' ');
      }
    ​
      print('Looking up articles about "$articleTitle". Please wait.');
      print('Here ya go!');
      print('(Pretend this is an article about "$articleTitle")');
    }
    ​
    // ... (your existing printUsage() function)
    

    Destaques do código anterior:

    • void searchWikipedia(List<String>? arguments) async: A função agora é async. Isso é essencial porque ela vai chamar getWikipediaArticle, que é uma função async e precisará de await no resultado dela.
  2. Rejeite entrada null ou string vazia: Atualmente, se o usuário executar dart bin/cli.dart search e pressionar Enter sem digitar um título, stdin.readLineSync() retorna uma string vazia. Se o usuário pressionar Control+D ou Control+Z para enviar um sinal de fim de arquivo (EOF), stdin.readLineSync() retorna null, e ?? '' usa uma string vazia como fallback. Nos dois casos, a CLI faz uma requisição simulada à Wikipedia com uma string vazia como título do artigo.

    Substitua o fallback ?? '' por uma verificação que exibe uma mensagem e encerra a função quando nenhuma entrada válida é fornecida.

    cli/bin/cli.dart
    dart
    void searchWikipedia(List<String>? arguments) async {
      final String articleTitle;
    ​
      if (arguments == null || arguments.isEmpty) {
        print('Please provide an article title.');
        // Read input without the `?? ''` fallback.
        final inputFromStdin = stdin.readLineSync();
        if (inputFromStdin == null || inputFromStdin.isEmpty) {
          print('No article title provided. Exiting.');
          return; // Exit the function if there's no valid input.
        }
        articleTitle = inputFromStdin;
      } else {
        // Otherwise, join the arguments into a single string.
        articleTitle = arguments.join(' ');
      }
    ​
      print('Looking up articles about "$articleTitle". Please wait.');
      print('Here ya go!');
      print('(Pretend this is an article about "$articleTitle")');
    }
    
  3. Chame getWikipediaArticle e exiba o resultado: Substitua as duas linhas print de placeholder no final de searchWikipedia por uma chamada a getWikipediaArticle e um único print do resultado.

    cli/bin/cli.dart
    dart
    void searchWikipedia(List<String>? arguments) async {
      final String articleTitle;
    ​
      if (arguments == null || arguments.isEmpty) {
        print('Please provide an article title.');
        final inputFromStdin = stdin.readLineSync();
        if (inputFromStdin == null || inputFromStdin.isEmpty) {
          print('No article title provided. Exiting.');
          return;
        }
        articleTitle = inputFromStdin;
      } else {
        articleTitle = arguments.join(' ');
      }
    ​
      print('Looking up articles about "$articleTitle". Please wait.');
    ​
      // Call the API and await the result.
      var articleContent = await getWikipediaArticle(articleTitle);
      print(articleContent); // Print the full article response (raw JSON for now)
    }
    

    Destaques do código anterior:

    • await getWikipediaArticle(articleTitle): Como getWikipediaArticle é uma função async, você precisa usar await no resultado dela. Isso pausa a função searchWikipedia até que o Future<String> retornado por getWikipediaArticle seja resolvido em uma String com o conteúdo do artigo.
    • print(articleContent): Imprime o resumo do artigo obtido como uma string JSON bruta no console.

Tarefa 5: Atualize o main para chamar searchWikipedia

#

Por fim, atualize sua função main para chamar a nova função searchWikipedia quando o comando wikipedia for usado.

  1. Localize o bloco else if na sua função main que atualmente trata o comando search. Altere o nome do comando de search para wikipedia e atualize a chamada da função.

    No código de exemplo, main não usa await na chamada a searchWikipedia, o que significa que main em si não precisa ser marcada como async.

    Sua função main deve ficar assim:

    cli/bin/cli.dart
    dart
    // ... (existing const version declaration and printUsage function)
    ​
    void main(List<String> arguments) {
      if (arguments.isEmpty || arguments.first == 'help') {
        printUsage();
      } else if (arguments.first == 'version') {
        print('Dartpedia CLI version $version');
      } else if (arguments.first == 'wikipedia') { // Changed to 'wikipedia'
        // Pass all arguments *after* 'wikipedia' to searchWikipedia
        final inputArgs = arguments.length > 1 ? arguments.sublist(1) : null;
        searchWikipedia(inputArgs); // Call searchWikipedia (no 'await' needed here for main)
      } else {
        printUsage(); // Catch all for any unrecognized command.
      }
    }
    
    • arguments.sublist(1): Isso extrai todos os elementos da lista arguments, começando no segundo elemento (índice 1). Assim, o próprio comando wikipedia é removido, e searchWikipedia recebe apenas os argumentos com o título do artigo.
    • searchWikipedia(inputArgs): Essa linha chama searchWikipedia diretamente. Como main não precisa fazer nada depois que searchWikipedia termina, você não precisa usar await nela a partir de main. Por isso, main não precisa ser async.

Tarefa 6: Execute o aplicativo

#

Agora que você implementou a requisição http e a integrou ao seu aplicativo, teste-o.

  1. Abra seu terminal e execute o seguinte comando:

    bash
    dart run bin/cli.dart wikipedia "Dart_(programming_language)"
    
  2. Verifique se o aplicativo buscou o resumo do artigo "Dart" na API da Wikipedia e imprimiu o JSON bruto da resposta no console. Você pode ver algo como:

    bash
    Looking up articles about "Dart_(programming_language)". Please wait.
    {
      "type": "standard",
      "title": "Dart (programming language)",
      "displaytitle": "<span class=\"mw-page-title-main\">Dart (programming language)</span>",
      "namespace": {
          "id": 0,
          "text": ""
        }
    
      // ... (rest of the JSON output will be present but truncated here)
    
    }
    
  3. Em seguida, tente executar sem argumentos. Por exemplo, quando solicitado, digite "Flutter_(software)".

    bash
    dart run bin/cli.dart wikipedia
    
    bash
    Please provide an article title.
    Flutter_(software)
    Looking up articles about "Flutter_(software)". Please wait.
    {
      "type": "standard",
      "title": "Flutter (software)",
      "displaytitle": "<span class=\"mw-page-title-main\">Flutter (software)</span>",
      "namespace": {
          "id": 0,
          "text": ""
      }
    
    // ... (rest of the JSON output will be present but truncated here)
    
    }
    

    Você implementou com sucesso o comando wikipedia básico, que busca dados reais de uma API externa!

Revisão

#

O que você concluiu

Aqui está um resumo do que você construiu e aprendeu nesta lição.
Dominou futures, async e await

Você aprendeu que Future representa um valor que estará disponível mais tarde. A keyword async marca funções como assíncronas, e await pausa a execução até que um Future seja concluído.

Adicionou o pacote http como dependência

Você modificou o pubspec.yaml para adicionar o pacote http, executou dart pub get para baixá-lo e o importou com um alias (as http). Você pode seguir estes passos para adicionar qualquer pacote externo do pub.dev.

Buscou dados em tempo real da Wikipedia

Você criou getWikipediaArticle() para fazer requisições HTTP GET usando a função get do package:http, construiu URIs, verificou status codes e retornou o corpo da resposta. Seu CLI agora busca dados reais da web!

Quiz

#

Check your understanding

1 / 4
Qual palavra-chave é usada para marcar uma função como assíncrona em Dart?
  1. async

    Isso mesmo!

    A palavra-chave async marca uma função como assíncrona, permitindo que ela use await para esperar operações assíncronas.

  2. await

    Não exatamente.

    await é usado dentro de funções assíncronas para pausar até o resultado. Ele não marca a função como assíncrona por si só.

  3. future

    Não exatamente.

    Future é um tipo que representa um valor que chegará depois, não uma palavra-chave para marcar funções.

  4. asynchronous

    Não exatamente.

    Embora seja descritivo, asynchronous não é uma palavra-chave do Dart. O Dart usa uma palavra-chave mais curta para marcar funções assíncronas.

O que um Future<String> representa em Dart?
  1. Um valor string que estará disponível em algum momento no futuro.

    Isso mesmo!

    Um Future é como uma promise. Ele representa um valor que ainda não está disponível, mas estará assim que uma operação assíncrona (como uma requisição de rede) terminar.

  2. Uma string que pode ser null.

    Não exatamente.

    Strings que podem ser nulas têm o tipo String?. Um Future<String> representa um valor string que estará disponível depois, quando a operação assíncrona terminar.

  3. Uma lista de strings que pode crescer com o tempo.

    Não exatamente.

    Uma lista de strings que pode crescer seria List<String>. Future<T> encapsula um único valor do tipo T, não uma coleção.

  4. Uma string armazenada em uma área separada da memória.

    Não exatamente.

    O tipo Future não tem relação com localização de memória ou armazenamento. Pense no que "future" significa na linguagem do dia a dia.

O que o await faz quando é colocado antes de um Future?
  1. Pausa a função até o Future completar e depois retorna o valor já desencapsulado.

    Isso mesmo!

    O await pausa a execução da função até que o Future seja concluído.

  2. Cancela o Future se ele demorar demais.

    Não exatamente.

    O await não cancela nada nem define timeouts. Ele apenas espera, independentemente de quanto a operação demorar.

  3. Executa o Future em uma thread separada para melhorar o desempenho.

    Não exatamente.

    O Dart não usa threads automaticamente dessa forma. O await funciona dentro do modelo de event loop de thread única do Dart.

  4. Converte o Future em uma operação síncrona.

    Não exatamente.

    A operação continua sendo assíncrona. O await muda a forma como você escreve o código, não como ele é executado.

Enquanto seu código espera uma resposta HTTP com await, o que acontece com as outras partes do seu aplicativo?
  1. Outras operações assíncronas e handlers de eventos podem continuar rodando.

    Isso mesmo!

    Esse é o principal benefício da programação assíncrona. Enquanto espera uma operação, o event loop do Dart pode tratar outras tarefas, mantendo seu app responsivo.

  2. Tudo trava até a resposta chegar.

    Não exatamente.

    Se tudo travasse, a programação assíncrona não traria nenhum benefício. Pense em por que a programação assíncrona existe.

  3. O Dart inicia automaticamente uma nova thread para cuidar da espera.

    Não exatamente.

    O Dart usa um modelo de event loop de thread única. O await funciona dentro desse modelo, sem criar novas threads.

  4. O programa é encerrado se a resposta demorar demais.

    Não exatamente.

    Programas não são encerrados por esperar operações assíncronas. Não existe um timeout embutido que cause esse encerramento.

Próxima lição

#

No próximo capítulo, você vai focar em organizar nosso código em bibliotecas e pacotes reutilizáveis. Você vai refatorar seu aplicativo para melhorar sua estrutura e manutenibilidade, criando um pacote separado para lidar com argumentos de linha de comando.

Encontrou essa página sem tradução ou que precisa de correção? Abra uma issue e nos ajude a manter esse site em PT-BR para ajudar pessoas como você.