Assíncronas e funções de tarefa

O FSharp.Core 11 adiciona funções para interoperabilidade de tarefas, transformações de resultados e concorrência limitada. Use-os com fluxos de trabalho assíncronos e expressões de tarefa já existentes; não requerem o protótipo assíncrono em tempo de execução.

Aguardar operações .NET a partir de assíncronas

Para iniciar uma operação .NET com o token de cancelamento do fluxo de trabalho assíncrono, passe uma ficha de 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
    }

O ficheiro lido recebe o token de cancelamento do fluxo de trabalho. Um ficheiro em falta 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 já tiver um valor semelhante a uma tarefa. Ambas as APIs propagam diretamente a única exceção subjacente de uma tarefa com falha. Um agregado com múltiplas exceções internas permanece um agregado. Async.AwaitTask mantém o seu comportamento existente de envolvimento de exceções.

API Formas de argumento/resultado apoiadas
Async.Await Task<'T> -> Async<'T>; Task -> Async<unit>
Async.Await ValueTask<'T> -> Async<'T>; ValueTask -> Async<unit>
Async.Await Sobrecarga semelhante à tarefa inline usando o GetAwaiter protocolo.
Async.StartTaskImmediate (CancellationToken -> Task<'T>) -> Async<'T>; retornos Async<unit>não genéricosTask.
Async.StartTaskImmediate Correspondentes ValueTask, ValueTask<'T>, e sobrecargas de fábrica semelhantes a tarefas.
Task.startAsyncImmediate CancellationToken -> Async<'T> -> Task<'T>
Async.RunSynchronouslyImmediate Async<'T> * ?cancellationToken: CancellationToken -> 'T

Async.RunSynchronouslyImmediate começa no thread que chama, ao contrário Async.RunSynchronously do thread em primeiro plano, e bloqueia para conclusão. Não tem parâmetro de timeout. Prefiro composição assíncrona em UI e threads de pedidos.

Expor um fluxo de trabalho assíncrono como uma tarefa

Para oferecer o fluxo de trabalho de leitura de ficheiros acima a chamadores baseados em tarefas, aceite um token de cancelamento e transmita-o Task.startAsyncImmediate:

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

A ponte retorna Task<string option> e propaga o cancelamento. Começa na thread de chamada até à primeira suspensão; as continuações posteriores não precisam de usar essa thread.

Concorrência ligada

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

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 começa, no máximo, o limite positivo dos cálculos em simultâneo e retorna os resultados na ordem de entrada. A ordem de início e a ordem de conclusão não são especificadas. Para concorrência maior do que um, cada fábrica recebe um token ligado que também sinaliza falha entre irmãos; para execução sequencial, recebe diretamente o token fornecido.

Quando um cálculo falha, solicita-se o cancelamento para os seus irmãos. As operações devem observar os seus tokens para cooperar. Se vários cálculos falharem, propaga-se uma exceção; não dependam de qual falha vence.

Function Forma característica
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 transmitem o token de cancelamento ambiente do fluxo de trabalho. Existentes Async.Parallel (incluindo a opção de limite de concorrência) e Async.Sequential continuam disponíveis. As Do variantes revelam um unit resultado quando só precisas de completar. Um limite de concorrência não é um limitador de taxa: operações rápidas ainda podem produzir muitos pedidos por segundo.

Resultados de transformação e recuperação de falhas

Os módulos Microsoft.FSharp.Control.Async, Task, e ValueTask expõem estas funções qualificadas. M<'T> na coluna shape representa o tipo de computação correspondente, não uma abreviatura do tipo F#.

Funções qualificadas Shape
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>; é necessário argumento explícito do tipo de entrada.
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, mapear o conteúdo de um ficheiro para um comprimento, depois representar 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 ficheiro contendo Report 1 fornece Ok 8. Um ficheiro em falta dá a um Error contendo um FileNotFoundException. Para descartar um valor bem-sucedido em vez de o transformar, especifique o tipo de entrada quando 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 não relacionada com cancelamento; não executa outro cálculo assíncrono. catch retorna Ok ou Error. O cancelamento não é convertido num valor de erro nem passado para o handler de recuperação. Task.empty é um Task<unit>, não o não-genérico Task.CompletedTask.

Ao contrário das minúsculas Async.catch, os retornos Choiceexistentes Async.Catch . A recuperação trata das falhas do cálculo fornecido, não das exceções síncronas lançadas antes da chamada. O assíncrono mantém-se adiado; as tarefas continuam ansiosas.

ValueTask e disponibilidade da biblioteca

As APIs do ValueTask requerem a netstandard2.1 build net10.0 ou do FSharp.Core 11. Ver seleção de alvos principais.

Observe o contrato de consumo único ValueTask: consuma cada valor uma vez, não através de awaits ou pipelines separados. Para esperas repetidas, chame Task.ofValueTask uma vez e reuse apenas a tarefa resultante.

Ver também