Funciones asincrónicas y de tareas

FSharp.Core 11 agrega funciones para la interoperabilidad de tareas, las transformaciones de resultados y la simultaneidad limitada. Úselas con flujos de trabajo asincrónicos y expresiones de tareas existentes; no requieren el prototipo en tiempo de ejecución asincrónico.

Await .NET operaciones desde asincrónico

Para iniciar una operación de .NET con el token de cancelación del flujo de trabajo asincrónico, pase un generador a Async.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
    }

La lectura del archivo recibe el token de cancelación del flujo de trabajo. Un archivo que falta devuelve None; una lectura correcta devuelve Some contents. El flujo de trabajo espera la finalización, incluida la cancelación y la limpieza.

Úselo Async.Await cuando ya tenga un valor similar a una tarea. Ambas API propagan directamente la única excepción subyacente de una tarea con errores. Un agregado con varias excepciones internas sigue siendo un agregado. Async.AwaitTask mantiene su comportamiento de ajuste de excepciones existente.

API Formas de argumento/resultado admitidas
Async.Await Task<'T> -> Async<'T>; Task -> Async<unit>
Async.Await ValueTask<'T> -> Async<'T>; ValueTask -> Async<unit>
Async.Await Sobrecarga similar a tareas insertadas mediante el GetAwaiter protocolo .
Async.StartTaskImmediate (CancellationToken -> Task<'T>) -> Async<'T>; no genérico Task devuelve Async<unit>.
Async.StartTaskImmediate Las sobrecargas de fábrica de tareas , ValueTask<'T>y correspondientesValueTask.
Task.startAsyncImmediate CancellationToken -> Async<'T> -> Task<'T>
Async.RunSynchronouslyImmediate Async<'T> * ?cancellationToken: CancellationToken -> 'T

Async.RunSynchronouslyImmediate se inicia en el subproceso que realiza la llamada, a diferencia Async.RunSynchronously de en un subproceso en primer plano, y bloquea la finalización. No tiene ningún parámetro de tiempo de espera. Prefiere la composición asincrónica en la interfaz de usuario y los subprocesos de solicitud.

Exponer un flujo de trabajo asincrónico como una tarea

Para ofrecer el flujo de trabajo de lectura de archivos anterior a los autores de llamadas basados en tareas, acepte un token de cancelación y páselo a través de Task.startAsyncImmediate:

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

El puente devuelve Task<string option> y propaga la cancelación. Se inicia en el subproceso que llama hasta la primera suspensión; Las continuaciones posteriores no necesitan usar ese subproceso.

Simultaneidad enlazada

Un Async<'T> valor describe el trabajo diferido. Normalmente, una tarea representa una operación que ya se ha iniciado. Para limitar el uso de recursos, describa el trabajo de tareas como factorías en lugar de tareas ya en ejecución:

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 comienza como máximo el límite positivo de cálculos simultáneamente y devuelve resultados en orden de entrada. El orden de inicio y el orden de finalización no se especifican. Para la simultaneidad mayor que una, cada fábrica recibe un token vinculado que también indica un error relacionado; para la ejecución secuencial recibe el token proporcionado directamente.

Cuando se produce un error de cálculo, se solicita la cancelación para sus elementos del mismo nivel. Las operaciones deben observar sus tokens para cooperar. Si se produce un error en varios cálculos, se propaga una excepción; no dependen de qué fracaso gana.

Función Forma de firma
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>

Las Async funciones fluyen el token de cancelación ambiental del flujo de trabajo. Existente Async.Parallel (incluida su opción de límite de simultaneidad) y Async.Sequential permanece disponible. Las Do variantes exponen un unit resultado cuando solo necesita finalización. Un límite de simultaneidad no es un limitador de velocidad: las operaciones rápidas pueden producir muchas solicitudes por segundo.

Transformación de resultados y recuperación de errores

Los Microsoft.FSharp.Control.Asyncmódulos , Tasky ValueTask exponen estas funciones calificadas. M<'T> en la columna shape representa el tipo de cálculo correspondiente, no una abreviatura de tipo F#.

Funciones calificadas 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>; se requiere un argumento de tipo de entrada explícito.
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 ejemplo, asigne el contenido de un archivo a una longitud y, a continuación, represente un error de lectura como :Result

open System.IO
open System.Threading

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

Un archivo que contiene Report 1 proporciona Ok 8. Un archivo que falta proporciona un Error objeto que contiene .FileNotFoundException Para descartar un valor correcto en lugar de transformarlo, especifique el tipo de entrada al llamar a ignore:

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

catchWith proporciona un valor de reemplazo para un error de no cancelación; no ejecuta otro cálculo asincrónico. catch devuelve Ok o Error. La cancelación no se convierte en un valor de error ni se pasa al controlador de recuperación. Task.empty es , Task<unit>no es el no genérico Task.CompletedTask.

A diferencia de en minúsculas Async.catch, existing Async.Catch devuelve Choice. La recuperación controla los errores del cálculo proporcionado, no las excepciones sincrónicas que se producen antes de la llamada. Async permanece diferido; las tareas permanecen diligentes.

Disponibilidad de ValueTask y biblioteca

Las API de ValueTask requieren la netstandard2.1 compilación o net10.0 de FSharp.Core 11. Consulte Selección de destino principal.

Observe el contrato valueTask de consumo único: consume cada valor una vez, no a través de esperas o canalizaciones independientes. Para las esperas repetidas, llame Task.ofValueTask una vez y reutilice solo la tarea resultante.

Consulte también