Expressões assíncronas
Este artigo descreve o suporte em F# para expressões assíncronas. As expressões assíncronas fornecem uma maneira de executar cálculos de forma assíncrona, ou seja, sem bloquear a execução de outro trabalho. Por exemplo, cálculos assíncronos podem ser usados para gravar aplicativos com UIs que permanecem responsivos aos usuários à medida que o aplicativo executa outro trabalho. O modelo de programação do Workflow Assíncrono F# permite escrever programas funcionais enquanto oculta os detalhes da transição de threads dentro de uma biblioteca.
O código assíncrono também pode ser criado usando expressões de tarefa , que criam tarefas do .NET diretamente. O uso de expressões de tarefa é preferencial ao interoperar extensivamente com bibliotecas .NET que criam ou consomem tarefas do .NET. Ao escrever o código mais assíncrono em F#, as expressões assíncronas F# são preferenciais porque são mais sucintas, mais composicionais e evitam certas ressalvas associadas às tarefas do .NET.
Sintaxe
async { expression }
Observações
Na sintaxe anterior, a computação representada por expression
é configurada para ser executada de forma assíncrona, ou seja, sem bloquear o thread de computação atual quando operações de suspensão assíncronas, E/S e outras operações assíncronas são executadas. Cálculos assíncronos geralmente são iniciados em um thread em segundo plano enquanto a execução continua no thread atual. O tipo da expressão é Async<'T>
, em que 'T
é o tipo retornado pela expressão quando a palavra-chave return
é usada.
A classe Async
fornece métodos que dão suporte a vários cenários. A abordagem geral é criar Async
objetos que representam a computação ou os cálculos que você deseja executar de forma assíncrona e, em seguida, iniciar esses cálculos usando uma das funções de gatilho. O gatilho usado depende se você deseja usar o thread atual, um thread em segundo plano ou um objeto de tarefa .NET. Por exemplo, para iniciar uma computação assíncrona no thread atual, você pode usar Async.StartImmediate
. Quando você inicia uma computação assíncrona do thread da interface do usuário, não bloqueia o loop de eventos principal que processa ações do usuário, como pressionamentos de teclas e atividade do mouse, para que seu aplicativo permaneça responsivo.
Associação Assíncrona usando LET!
Em uma expressão assíncrona, algumas expressões e operações são síncronas e outras são assíncronas. Quando você chama um método de forma assíncrona, em vez de uma associação de let
comum, você usa let!
. O efeito de let!
é habilitar a execução para continuar em outras computações ou threads, à medida que a computação está sendo executada. Depois que o lado direito da associação let!
é retornado, o restante da expressão assíncrona retoma a execução.
O código a seguir mostra a diferença entre let
e let!
. A linha de código que usa let
apenas cria uma computação assíncrona como um objeto que você pode executar posteriormente usando, por exemplo, Async.StartImmediate
ou Async.RunSynchronously
. A linha de código que usa let!
inicia a computação e executa uma espera assíncrona: o thread é suspenso até que o resultado esteja disponível, momento em que a execução continua.
// let just stores the result as an asynchronous operation.
let (result1 : Async<byte[]>) = stream.AsyncRead(bufferSize)
// let! completes the asynchronous operation and returns the data.
let! (result2 : byte[]) = stream.AsyncRead(bufferSize)
let!
só pode ser usado para aguardar computações assíncronas do F# Async<T>
diretamente. Você pode aguardar outros tipos de operações assíncronas indiretamente:
- Tarefas do .NET, Task<TResult> e o Task não genérico, combinando com
Async.AwaitTask
- Tarefas de valor do .NET, ValueTask<TResult> e o ValueTask não genérico, combinando com
.AsTask()
eAsync.AwaitTask
- Qualquer objeto que segue o padrão "GetAwaiter" especificado no F# RFC FS-1097, quando combinado com
task { return! expr } |> Async.AwaitTask
.
Fluxo de controle
Expressões assíncronas podem incluir constructos de fluxo de controle, como for .. in .. do
, while .. do
, try .. with ..
, try .. finally ..
, if .. then .. else
e if .. then ..
. Eles podem, por sua vez, incluir construções assíncronas adicionais, com exceção dos manipuladores with
e finally
, que são executados de forma síncrona.
As expressões assíncronas do F# não dão suporte a try .. finally ..
assíncrono. Você pode usar uma expressão de tarefa para esse caso.
Associações use
e use!
Dentro de expressões assíncronas, as associações de use
podem ser associadas a valores do tipo IDisposable. Nesse último caso, a operação de limpeza de descarte é executada de forma assíncrona.
Além de let!
, você pode usar use!
para executar associações assíncronas. A diferença entre let!
e use!
é a mesma que a diferença entre let
e use
. Para use!
, o objeto é descartado no fechamento do escopo atual. Observe que, na versão atual do F#, use!
não permite que um valor seja inicializado como nulo, mesmo que use
o faça.
Primitivos assíncronos
Um método que executa uma única tarefa assíncrona e retorna o resultado é chamado de primitivo assíncrono, e eles são projetados especificamente para uso com let!
. Diversos primitivos assíncronos são definidos na biblioteca central de F#. Dois desses métodos para aplicativos Web são definidos no módulo FSharp.Control.WebExtensions
: WebRequest.AsyncGetResponse
e HttpClient.GetStringAsync
(encapsulados com Async.AwaitTask
para compatibilidade com o modelo assíncrono do F#). Ambos os primitivos baixam dados de uma página da Web, dada uma URL. AsyncGetResponse
produz um objeto System.Net.WebResponse
e GetStringAsync
produz uma cadeia de caracteres que representa o HTML para uma página da Web.
Vários primitivos para operações assíncronas de E/S são incluídos no módulo FSharp.Control.CommonExtensions
. Esses métodos de extensão da classe System.IO.Stream
são Stream.AsyncRead
e Stream.AsyncWrite
.
Você também pode escrever seus próprios primitivos assíncronos definindo uma função ou método cujo corpo é uma expressão assíncrona.
Para usar métodos assíncronos no .NET Framework projetados para outros modelos assíncronos com o modelo de programação assíncrona F#, crie uma função que retorna um objeto Async
F#. A biblioteca F# tem funções que facilitam essa tarefa.
Um exemplo de uso de expressões assíncronas está incluído aqui; há muitos outros na documentação para os métodos da classe assíncrona .
Este exemplo mostra como usar expressões assíncronas para executar código em paralelo.
No exemplo de código a seguir, uma função fetchAsync
obtém o texto HTML retornado de uma solicitação da Web. A função fetchAsync
contém um bloco assíncrono de código. Quando uma associação é feita ao resultado de um primitivo assíncrono, nesse caso AsyncDownloadString
, let!
é usado em vez de let
.
Você usa a função Async.RunSynchronously
para executar uma operação assíncrona e aguardar o resultado. Por exemplo, você pode executar várias operações assíncronas em paralelo usando a função Async.Parallel
junto com a função Async.RunSynchronously
. A função Async.Parallel
usa uma lista dos objetos Async
, configura o código para cada objeto de tarefa Async
a ser executado em paralelo e retorna um objeto Async
que representa a computação paralela. Assim como para uma única operação, você chama Async.RunSynchronously
para iniciar a execução.
A função runAll
inicia três expressões assíncronas em paralelo e aguarda até que todas elas sejam concluídas.
open System.Net
open Microsoft.FSharp.Control.WebExtensions
open System.Net.Http
let urlList = [ "Microsoft.com", "http://www.microsoft.com/"
"MSDN", "http://msdn.microsoft.com/"
"Bing", "http://www.bing.com"
]
let fetchAsync(name, url:string) =
async {
try
let uri = new System.Uri(url)
let httpClient = new HttpClient()
let! html = httpClient.GetStringAsync(uri) |> Async.AwaitTask
printfn "Read %d characters for %s" html.Length name
with
| ex -> printfn "%s" (ex.Message);
}
let runAll() =
urlList
|> Seq.map fetchAsync
|> Async.Parallel
|> Async.RunSynchronously
|> ignore
runAll()
Consulte também
- Programação assíncrona em F#
- Referência da Linguagem F#
- expressões computacionais
- Expressões de tarefa
- Classe Control.Async