Funções assíncronas e de tarefa

O FSharp.Core 11 adiciona funções para interoperabilidade de tarefas, transformações de resultado e simultaneidade limitada. Use-os com fluxos de trabalho assíncronos e expressões de tarefa existentes; eles não exigem o protótipo assíncrono de runtime.

Aguardar operações de .NET da assíncrona

Para iniciar uma operação de .NET com o token de cancelamento do fluxo de trabalho assíncrono, passe uma fábrica paraAsync.StartTaskImmediate:

open System.IO

let tryReadFile path =
    async {
        try
            let! contents =
                Async.StartTaskImmediate(fun ct -> File.ReadAllTextAsync(path, ct))
            return Some contents
        with :? FileNotFoundException ->
            return None
    }

A leitura do arquivo recebe o token de cancelamento do fluxo de trabalho. Um arquivo ausente retorna None; uma leitura bem-sucedida retorna Some contents. O fluxo de trabalho aguarda a conclusão, incluindo cancelamento e limpeza.

Use Async.Await quando você já tiver um valor semelhante a uma tarefa. Ambas as APIs propagam diretamente a única exceção subjacente de uma tarefa com falha. Uma agregação com várias exceções internas permanece uma agregação. Async.AwaitTask mantém seu comportamento de encapsulamento de exceção existente.

API Formas de argumento/resultado com suporte
Async.Await Task<'T> -> Async<'T>; Task -> Async<unit>
Async.Await ValueTask<'T> -> Async<'T>; ValueTask -> Async<unit>
Async.Await Sobrecarga embutida como tarefa usando o GetAwaiter protocolo.
Async.StartTaskImmediate (CancellationToken -> Task<'T>) -> Async<'T>; retornos não genéricos TaskAsync<unit>.
Async.StartTaskImmediate ValueTask<'T>Sobrecargas de fábrica correspondentes ValueTaske semelhantes a tarefas.
Task.startAsyncImmediate CancellationToken -> Async<'T> -> Task<'T>
Async.RunSynchronouslyImmediate Async<'T> * ?cancellationToken: CancellationToken -> 'T

Async.RunSynchronouslyImmediate começa no thread de chamada, ao contrário Async.RunSynchronously de um thread em primeiro plano e bloqueia a conclusão. Ele não tem parâmetro de tempo limite. Prefira a composição assíncrona nos threads de interface do usuário e solicitação.

Expor um fluxo de trabalho assíncrono como uma tarefa

Para oferecer o fluxo de trabalho de leitura de arquivo acima para chamadores baseados em tarefas, aceite um token de cancelamento e passe-o:Task.startAsyncImmediate

let tryReadFileTask (path: string) (ct: CancellationToken) =
    tryReadFile path |> Task.startAsyncImmediate ct

A ponte retorna Task<string option> e propaga o cancelamento. Ele começa no thread de chamada até a primeira suspensão; continuações posteriores não precisam usar esse thread.

Simultaneidade associada

Um Async<'T> valor descreve o trabalho adiado. Uma tarefa geralmente representa uma operação que já foi iniciada. Para limitar o uso de recursos, descreva o trabalho da tarefa como fábricas em vez de tarefas já em execução:

open System.IO
open System.Threading

let readFiles (paths: seq<string>) (ct: CancellationToken) =
    paths
    |> Seq.map (fun path ->
        fun token -> File.ReadAllTextAsync(path, token))
    |> Task.parallelLimit 4 ct

Task.parallelLimit inicia no máximo o limite positivo de cálculos simultaneamente e retorna resultados na ordem de entrada. A ordem de início e a ordem de conclusão não são especificadas. Para simultaneidade maior que uma, cada fábrica recebe um token vinculado que também sinaliza falha entre irmãos; para execução sequencial, ele recebe o token fornecido diretamente.

Quando uma falha de computação é solicitada, o cancelamento é solicitado para seus irmãos. As operações devem observar seus tokens para cooperar. Se vários cálculos falharem, uma exceção será propagada; não depende de qual falha ganha.

Function Forma da assinatura
Task.parallelLimit int -> CancellationToken -> seq<CancellationToken -> Task<'T>> -> Task<'T[]>
Task.parallelDoLimit int -> CancellationToken -> seq<CancellationToken -> Task<unit>> -> Task<unit>
Task.sequential CancellationToken -> seq<CancellationToken -> Task<'T>> -> Task<'T[]>
Task.sequentialDo CancellationToken -> seq<CancellationToken -> Task<unit>> -> Task<unit>
Async.parallelLimit int -> seq<Async<'T>> -> Async<'T[]>
Async.parallelDoLimit int -> seq<Async<unit>> -> Async<unit>
Async.sequentialDo seq<Async<unit>> -> Async<unit>

As Async funções fluem o token de cancelamento ambiente do fluxo de trabalho. Existente Async.Parallel (incluindo sua opção de limite de simultaneidade) e Async.Sequential permanecer disponível. As Do variantes expõem um unit resultado quando você só precisa de conclusão. Um limite de simultaneidade não é um limitador de taxa: operações rápidas ainda podem produzir muitas solicitações por segundo.

Transformar resultados e recuperar-se de falhas

Os Microsoft.FSharp.Control.Asyncmódulos e ValueTask os módulos Taskexpõem essas funções qualificadas. M<'T> na coluna de forma significa o tipo de computação correspondente, não uma abreviação de tipo F#.

Funções qualificadas Forma
Async.result, Task.result, ValueTask.result 'T -> M<'T>
Async.map, Task.map, ValueTask.map ('T -> 'U) -> M<'T> -> M<'U>
Async.bind, Task.bind, ValueTask.bind ('T -> M<'U>) -> M<'T> -> M<'U>
Async.ignore<'T>, Task.ignore<'T>, ValueTask.ignore<'T> M<'T> -> M<unit>; argumento de tipo de entrada explícito necessário.
Async.catchWith, Task.catchWith, ValueTask.catchWith (exn -> 'T) -> M<'T> -> M<'T>
Async.catch, Task.catch, ValueTask.catch M<'T> -> M<Result<'T, exn>>
Async.empty, Task.empty, ValueTask.empty M<unit>
Task.ofValueTask ValueTask<'T> -> Task<'T>
ValueTask.ofTask Task<'T> -> ValueTask<'T>

Por exemplo, mapeie o conteúdo de um arquivo para um comprimento e, em seguida, represente uma falha de leitura como um Result:

open System.IO
open System.Threading

let fileLength (path: string) (ct: CancellationToken) =
    File.ReadAllTextAsync(path, ct)
    |> Task.map String.length
    |> Task.catch

Um arquivo que contém Report 1 fornece Ok 8. Um arquivo ausente fornece um ErrorFileNotFoundException. Para descartar um valor bem-sucedido em vez de transformá-lo, especifique o tipo de entrada ao chamar ignore:

let completion: System.Threading.Tasks.Task<unit> =
    Task.result 42 |> Task.ignore<int>

catchWith fornece um valor de substituição para uma falha de não cancelamento; ele não executa outra computação assíncrona. catch retorna Ok ou Error. O cancelamento não é convertido em um valor de erro ou passado para o manipulador de recuperação. Task.empty é um Task<unit>, não o não genérico Task.CompletedTask.

Ao contrário de minúsculas, os retornos existentes Async.catchAsync.CatchChoice. A recuperação lida com falhas da computação fornecida, não exceções síncronas geradas antes da chamada. Assíncrono permanece adiado; as tarefas permanecem ansiosas.

Disponibilidade de ValueTask e biblioteca

As APIs ValueTask exigem o build ou net10.0 o netstandard2.1 build do FSharp.Core 11. Consulte a seleção de destino do Core.

Observe o contrato de consumo único do ValueTask: consuma cada valor uma vez, não por meio de esperas ou pipelines separados. Para esperas repetidas, chame Task.ofValueTask uma vez e reutilize apenas a tarefa resultante.

Consulte também