Partilhar via


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 outros trabalhos. Por exemplo, cálculos assíncronos podem ser usados para escrever aplicativos que tenham interfaces do usuário que permaneçam responsivas aos usuários à medida que o aplicativo executa outro trabalho. O modelo de programação F# Asynchronous Workflows permite escrever programas funcionais enquanto oculta os detalhes da transição de threads em uma biblioteca.

O código assíncrono também pode ser criado usando expressões de tarefa, que criam tarefas .NET diretamente. O uso de expressões de tarefa é preferível ao interoperar extensivamente com bibliotecas .NET que criam ou consomem tarefas .NET. Ao escrever a maioria dos códigos assíncronos em F#, as expressões assíncronas F# são preferidas porque são mais sucintas, mais composicionais e evitam certas ressalvas associadas a tarefas .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. Os 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>, onde 'T é o tipo retornado pela expressão quando a return palavra-chave é usada.

A Async classe fornece métodos que oferecem suporte a vários cenários. A abordagem geral é criar Async objetos que representem a computação ou cálculos que você deseja executar de forma assíncrona e, em seguida, iniciar esses cálculos usando uma das funções de acionamento. O acionamento que você usa depende se você deseja usar o thread atual, um thread em segundo plano ou um objeto de tarefa .NET. Por exemplo, para iniciar um cálculo assíncrono no thread atual, você pode usar Async.StartImmediate. Ao iniciar uma computação assíncrona a partir do thread da interface do usuário, você não bloqueia o loop de evento principal que processa ações do usuário, como pressionamentos de teclas e atividade do mouse, para que seu aplicativo permaneça responsivo.

Vinculação assíncrona usando let!

Em uma expressão assíncrona, algumas expressões e operações são síncronas e outras assíncronas. Quando você chama um método de forma assíncrona, em vez de uma associação comum let , você usa let!. O efeito de let! é permitir que a execução continue em outros cálculos ou threads à medida que a computação está sendo executada. Depois que let! o lado direito da associação retorna, 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 o cálculo 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 cálculos assíncronos Async<T> F# diretamente. Você pode aguardar outros tipos de operações assíncronas indiretamente:

  • Tarefas Task<TResult> .NET e as não genéricas Task, combinando com Async.AwaitTask
  • Tarefas ValueTask<TResult> de valor .NET e as tarefas não genéricas ValueTask, combinando com .AsTask() e Async.AwaitTask
  • Qualquer objeto que siga o padrão "GetAwaiter" especificado em F# RFC FS-1097, combinando com task { return! expr } |> Async.AwaitTask.

Fluxo de Controlo

As expressões assíncronas podem incluir construções de fluxo de controle, como for .. in .. do, while .. do, try .. with .., try .. finally .., if .. then .. elsee if .. then ... Estes, por sua vez, podem incluir outras construções assíncronas with , com exceção dos manipuladores e finally , que são executados de forma síncrona.

As expressões assíncronas F# não suportam assíncronas.try .. finally .. Você pode usar uma expressão de tarefa para este caso.

use e use! vinculações

Dentro de expressões assíncronas, use as associações podem se vincular a valores do tipo IDisposable. Para este último, a operação de limpeza do descarte é executada de forma assíncrona.

Além do 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 use que isso aconteç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!o . Várias primitivas assíncronas são definidas na biblioteca principal do F#. Dois desses métodos para aplicativos Web são definidos no módulo FSharp.Control.WebExtensions: WebRequest.AsyncGetResponse e WebClient.AsyncDownloadString. Ambos os primitivos baixam dados de uma página da Web, dada uma URL. AsyncGetResponse produz um System.Net.WebResponse objeto e AsyncDownloadString produz uma cadeia de caracteres que representa o HTML de uma página da Web.

Várias primitivas para operações de E/S assíncronas estão incluídas no FSharp.Control.CommonExtensions módulo. Estes métodos de extensão da System.IO.Stream classe são Stream.AsyncRead e Stream.AsyncWrite.

Você também pode escrever suas próprias primitivas assíncronas definindo uma função ou método cujo corpo é uma expressão assíncrona.

Para usar métodos assíncronos no .NET Framework que são projetados para outros modelos assíncronos com o modelo de programação assíncrona F#, crie uma função que retorna um objeto F# Async . A biblioteca F# tem funções que facilitam isso.

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 Async.

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 fetchAsync função contém um bloco assíncrono de código. Quando uma ligação é feita para o resultado de uma primitiva assíncrona, neste caso AsyncDownloadString, let! é usado em vez de let.

Você usa a função Async.RunSynchronously para executar uma operação assíncrona e aguardar seu resultado. Como exemplo, você pode executar várias operações assíncronas em paralelo usando a Async.Parallel função junto com a Async.RunSynchronously função. A Async.Parallel função usa uma lista dos objetos, configura o código para que cada Async objeto de Async tarefa seja executado em paralelo e retorna um Async objeto que representa a computação paralela. Assim como para uma única operação, você chama Async.RunSynchronously para iniciar a execução.

A runAll função inicia três expressões assíncronas em paralelo e aguarda até que todas sejam concluídas.

open System.Net
open Microsoft.FSharp.Control.WebExtensions

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 webClient = new WebClient()
            let! html = webClient.AsyncDownloadString(uri)
            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