Programação assíncrona: futuros
Conteúdo
O que é importante:
- O código no Dart é executado em uma única execução de thread ( note thread - thread ).
- Devido ao código que retira (bloqueia) o encadeamento por um longo tempo, o programa pode congelar.
- Objetos
futures
( futures
) representam os resultados de operações assíncronas - processamento ou E / S, que serão concluídas posteriormente. - Para suspender a execução para conclusão no futuro, use
await
na função assíncrona (ou then()
ao usar a API do Future
). - Para detectar erros, use a construção
try-catch
(ou catchError()
ao usar a API do Future
) na função assíncrona. - Para processamento simultâneo, crie um isolado (ou trabalhador para o aplicativo da web).
O código no Dart é executado em um único thread de execução. Se o código estiver ocupado com cálculos longos ou estiver aguardando uma operação de E / S, o programa inteiro será pausado.
Operações assíncronas permitem que seu programa conclua outras tarefas enquanto aguarda a conclusão da operação. O Dart usa futures
para apresentar os resultados de operações assíncronas. Você também pode usar assíncrono e aguardar ou a API do futuro para trabalhar com futures
.
Uma nota
Todo o código é executado no contexto do isolado, que possui toda a memória usada pelo código. Mais de uma execução de código não pode ser iniciada no mesmo isolado.
Para execução paralela de blocos de código, você pode separá-los em isolados separados. (Os aplicativos da Web usam trabalhadores em vez de isolados.) Normalmente, cada um dos isolados é executado em seu próprio núcleo do processador. Os isolados não compartilham memória e a única maneira de interagir é enviar mensagens um ao outro. Para mergulhar no tópico, consulte a documentação para isolados ou trabalhadores .
1. Introdução
Vejamos um exemplo de código que pode "congelar" a execução do programa:
Nosso programa lê as notícias do arquivo do dia, exibe-as e exibe informações que ainda são do interesse do usuário:
<gathered news goes here> Winning lotto numbers: [23, 63, 87, 26, 2] Tomorrow's forecast: 70F, sunny. Baseball score: Red Sox 10, Yankees 0
Neste exemplo, o problema é que todas as operações após a chamada gatherNewsReports()
aguardarão até o gatherNewsReports()
retornar o conteúdo do arquivo, não importa quanto tempo leve. Se a leitura do arquivo demorar muito, o usuário será forçado a aguardar os resultados da loteria, a previsão do tempo e o vencedor de um jogo recente.
Para manter a capacidade de resposta do aplicativo, os autores do Dart usam um modelo assíncrono para identificar funções que executam um trabalho potencialmente caro. Tais funções retornam seu valor usando futures
.
Qual é o futuro?
future
- uma instância da classe Future <T> , que é uma operação assíncrona que retorna um resultado do tipo T. Se o resultado da operação não for usado, o tipo future
será indicado por Future<void>
. Ao chamar uma função que retorna future
, duas coisas acontecem:
- A função enfileira para execução e retorna um objeto
Future
incompleto. - Mais tarde, quando a operação estiver concluída, o
future
encerrado com um valor ou erro.
Para escrever um código dependente do future
, você tem duas opções:
- Use
async
- await
- Use a API
Future
Async - aguardar
As palavras-chave async
e await
fazem parte do suporte async
do Dart. Eles permitem escrever código assíncrono que se parece com código síncrono e não usa a API do Future
. Uma função assíncrona é uma função com a palavra async
chave async
na frente do corpo. A palavra-chave await
apenas funciona em funções assíncronas.
Nota: no Dart 1.x, funções assíncronas atrasam imediatamente a execução. No Dart 2, em vez de fazer uma pausa imediata, as funções assíncronas são executadas de forma síncrona até a primeira await
ou return
.
O código a seguir simula a leitura de notícias de um arquivo usando async
- await
. Abra o DartPad com o aplicativo , inicie e clique em CONSOLE para ver o resultado.
Observe que primeiro chamamos printDailyNewsDigest()
, mas as notícias são impressas por último, mesmo que o arquivo contenha apenas uma linha. Isso ocorre porque o código que lê e imprime o arquivo é executado de forma assíncrona.
Neste exemplo, printDailyNewsDigest()
faz uma chamada para gatherNewsReports()
, que é sem bloqueio. A chamada do método gatherNewsReports()
tarefa, mas não impede a execução do restante do código. O programa exibe os números da loteria, previsão e pontuação de um jogo de beisebol; O programa imprime as notícias após a coleta de gatherNewsReports()
. Se gatherNewsReports()
demorar algum tempo para concluir seu trabalho, nada de ruim acontece: o usuário pode ler outras coisas antes que o resumo diário de notícias seja impresso.
Preste atenção aos tipos de retorno. O tipo de retorno da função gatherNewsReports()
é Future<String>
, o que significa que retorna um future
que termina com um valor de sequência. A função printDailyNewsDigest()
, que não retorna um valor, tem um tipo de retorno Future<void>
.
O diagrama a seguir mostra as etapas de execução do código.

- O aplicativo começa a ser executado.
- A função
main()
é printDailyNewsDigest()
função assíncrona printDailyNewsDigest()
, que começa a executar de forma síncrona. printDailyNewsDigest()
usa await
para chamar a função gatherNewsReports()
, que começa a executar.gatherNewsReports()
retorna um future
inacabado (uma instância de Future<String>
).- Como
printDailyNewsDigest()
é uma função assíncrona e espera um valor, ele interrompe a execução e retorna o future
incompleto (neste caso, Future<void>
) para a função chamadora main ()
. - O restante das funções de saída são executadas. Como são síncronas, cada função é executada completamente antes de passar para a próxima. Por exemplo, todos os números ganhadores da loteria serão exibidos antes da previsão do tempo.
- Após a conclusão de
main()
funções assíncronas podem retomar a execução. Primeiro, obtemos o future
com notícias sobre a conclusão de gatherNewsReports()
. Em seguida, printDailyNewsDigest()
continua a execução, exibindo as notícias. - No final da execução
printDailyNewsDigest()
, o future
originalmente recebido future
e o aplicativo é encerrado.
Observe que a função assíncrona inicia imediatamente (de forma síncrona). A função pausa a execução e retorna um future
inacabado quando ocorrer a primeira ocorrência de qualquer um dos seguintes itens:
- A primeira
await
expressão (depois que a função obtém o future
incompleto dessa expressão). - Qualquer
return
em uma função. - O fim do corpo da função.
Tratamento de erros
Provavelmente você gostaria de "capturar" um erro na execução da função que retorna o future
. Nas funções assíncronas, você pode manipular erros usando o try-catch
:
Future<void> printDailyNewsDigest() async { try { var newsDigest = await gatherNewsReports(); print(newsDigest); } catch (e) {
Um try-catch
com código assíncrono se comporta da mesma forma que com código síncrono: se o código no bloco try
uma exceção, o código dentro do catch
é executado.
Execução sequencial
Você pode usar várias expressões de await
para garantir que cada instrução seja concluída antes de executar o seguinte:
expensiveB()
função expensiveB()
não é executada até que o expensiveA()
concluído e assim por diante.
API futura
Antes da adição de async
e await
no Dart 1.9, era necessário usar a API do Future
. Ainda é possível ver o uso da API do Future
no código antigo e no código que precisa de mais funcionalidade do que o async–await
para oferecer.
Para escrever código assíncrono usando a API do Future
, use o método then()
para registrar o retorno de chamada. Esse retorno de chamada funcionará quando o future
concluído.
O código a seguir simula a leitura de notícias de um arquivo usando a API do Future
. Abra o DartPad com o aplicativo , inicie e clique em CONSOLE para ver o resultado.
Observe que primeiro chamamos printDailyNewsDigest()
, mas as notícias são impressas por último, mesmo que o arquivo contenha apenas uma linha. Isso ocorre porque o código que lê e imprime o arquivo é executado de forma assíncrona.
Este aplicativo é executado da seguinte maneira:
- O aplicativo começa a ser executado.
- A função principal chama
printDailyNewsDigest()
, que não retorna o resultado imediatamente, mas chama primeiro gatherNewsReports()
. gatherNewsReports()
começa a ler notícias e retorna no future
.printDailyNewsDigest()
usa then()
para registrar um retorno de chamada que terá como parâmetro o valor obtido no final do future
. A chamada then()
retorna um novo future
, que termina com o valor retornado pelo retorno de chamada de then()
.- O restante das funções de saída são executadas. Como são síncronas, cada função é executada completamente antes de passar para a próxima. Por exemplo, todos os números ganhadores da loteria serão exibidos antes da previsão do tempo.
- Quando todas as notícias forem recebidas, o
future
retornado pela função gatherNewsReports()
termina com uma sequência que contém as notícias coletadas. - O código especificado em
then()
em printDailyNewsDigest()
é executado para printDailyNewsDigest()
notícias. - O aplicativo está sendo encerrado.
Nota: na função printDailyNewsDigest()
, o código future.then(print)
equivalente ao seguinte: future.then((newsDigest) => print(newsDigest))
.
Além disso, o código dentro de then()
pode usar chaves:
Future<void> printDailyNewsDigest() { final future = gatherNewsReports(); return future.then((newsDigest) { print(newsDigest);
Você deve especificar o argumento de retorno de chamada em then()
, mesmo se future
for do tipo Future<void>
. Por convenção, um argumento não utilizado é definido por meio de _
(sublinhado).
final future = printDailyNewsDigest(); return future.then((_) {
Tratamento de erros
Usando a API do Future
, você pode capturar o erro usando catchError()
:
Future<void> printDailyNewsDigest() => gatherNewsReports().then(print).catchError(handleError);
Se a notícia não for legível, o código acima será executado da seguinte maneira:
future
retornado por gatherNewsReports()
falha.future
retornado por then()
falha, print()
não print()
chamado.- O
catchError()
em catchError()
( handleError()
) captura o erro, o future
retornado por catchError()
concluído normalmente e o erro não se propaga mais.
A cadeia catchError()
- catchError()
é um padrão comum ao usar a API do Future
. Considere esse par como o equivalente a um try-catch
na API do Future
.
Assim como (), catchError () retorna um novo future
que termina com o valor de retorno do retorno de chamada. Para mergulhar no tópico, leia Futuros e manipulação de erros .
Chamando várias funções retornando o future
Vamos considerar três funções: expensiveA()
, expensiveB()
, expensiveC()
, que retornam o future
. Você pode chamá-los seqüencialmente (uma função inicia após a conclusão da anterior) ou pode executá-los todos ao mesmo tempo e fazer algo assim que todos os valores retornarem. A interface do futuro é flexível o suficiente para implementar os dois casos de uso.
Uma cadeia de chamadas de função usando then()
Quando as funções que retornam o future
devem ser executadas em ordem, use a cadeia a partir de then()
:
expensiveA() .then((aValue) => expensiveB()) .then((bValue) => expensiveC()) .then((cValue) => doSomethingWith(cValue));
Anexar retornos de chamada também funciona, mas é mais difícil de ler. ( nota http://callbackhell.com/ )
Aguardando a conclusão de vários futures
usando Future.wait()
Se a ordem de execução das funções não for importante, você poderá usar Future.wait()
. Quando você especifica a lista de futures
para parâmetros para a função Future.wait (), ela retorna imediatamente o future
. Esse future
não terminará até que todos os futures
especificados futures
. Este future
terminará com uma lista dos resultados de todos os futures
indicados.
Future.wait([expensiveA(), expensiveB(), expensiveC()]) .then((List responses) => chooseBestResponse(responses, moreInfo)) .catchError(handleError);
Se uma chamada para qualquer uma das funções falhar, o future
retornado por Future.wait()
também falhará. Use catchError()
para capturar esse erro.
O que mais ler?
Dart 2. Programação assíncrona: fluxos de dados