Fonctions asynchrones et de tâches

FSharp.Core 11 ajoute des fonctions pour l’interopérabilité des tâches, les transformations de résultats et la concurrence limitée. Utilisez-les avec des flux de travail asynchrones et des expressions de tâche existants ; ils ne nécessitent pas le prototype runtime-async.

Await .NET opérations à partir d’async

Pour démarrer une opération de .NET avec le jeton d'annulation du flux de travail asynchrone, passez une fabrique à 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
    }

Le fichier lu reçoit le jeton d’annulation du flux de travail. Un fichier manquant retourne None; une lecture réussie renvoie Some contents. Le flux de travail attend la fin, y compris l’annulation et le nettoyage.

Utilisez Async.Await quand vous disposez déjà d’une valeur semblable à une tâche. Les deux API propagent directement l’exception sous-jacente unique d’une tâche défectueuse. Un agrégat avec plusieurs exceptions internes reste un agrégat. Async.AwaitTask conserve son comportement d’habillage d’exception existant.

API Formes d’argument/résultat prises en charge
Async.Await Task<'T> -> Async<'T>; Task -> Async<unit>
Async.Await ValueTask<'T> -> Async<'T>; ValueTask -> Async<unit>
Async.Await Surcharge de type tâche inline à l’aide du GetAwaiter protocole.
Async.StartTaskImmediate (CancellationToken -> Task<'T>) -> Async<'T>; retours Async<unit>non génériques Task .
Async.StartTaskImmediate Surcharges d’usine correspondantes ValueTask, ValueTask<'T>et de type tâche.
Task.startAsyncImmediate CancellationToken -> Async<'T> -> Task<'T>
Async.RunSynchronouslyImmediate Async<'T> * ?cancellationToken: CancellationToken -> 'T

Async.RunSynchronouslyImmediate démarre sur le thread appelant, contrairement Async.RunSynchronously à un thread de premier plan, et bloque l’achèvement. Il n’a aucun paramètre de délai d’expiration. Préférer la composition asynchrone sur les threads d’interface utilisateur et de requête.

Exposer un flux de travail asynchrone en tant que tâche

Pour offrir le flux de travail de lecture de fichiers ci-dessus aux appelants basés sur des tâches, acceptez un jeton d’annulation et transmettez-le :Task.startAsyncImmediate

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

Le pont retourne Task<string option> et propage l’annulation. Il commence sur le thread appelant jusqu’à la première suspension ; les continuations ultérieures n’ont pas besoin d’utiliser ce thread.

Concurrence liée

Une Async<'T> valeur décrit le travail différé. Une tâche représente généralement une opération qui a déjà démarré. Pour limiter l’utilisation des ressources, décrivez le travail des tâches en tant que fabriques plutôt que les tâches en cours d’exécution :

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 commence simultanément à la limite positive des calculs et retourne des résultats dans l’ordre d’entrée. L’ordre de démarrage et l’ordre de saisie semi-automatique ne sont pas spécifiés. Pour l’accès concurrentiel supérieur à un, chaque fabrique reçoit un jeton lié qui signale également l’échec frère ; pour l’exécution séquentielle, il reçoit directement le jeton fourni.

Lorsqu’une erreur de calcul est demandée pour ses frères. Les opérations doivent observer leurs jetons pour coopérer. Si plusieurs calculs échouent, une exception est propagée ; ne dépend pas de l’échec qui gagne.

Fonction Forme de signature
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>

Les Async fonctions circulent le jeton d’annulation ambiant du flux de travail. Existant Async.Parallel (y compris son option de limite d’accès concurrentiel) et Async.Sequential reste disponible. Les Do variantes exposent un unit résultat lorsque vous n’avez besoin que de la saisie semi-automatique. Une limite de concurrence n’est pas un limiteur de débit : les opérations rapides peuvent toujours produire de nombreuses requêtes par seconde.

Transformer les résultats et récupérer à partir d’erreurs

Les Microsoft.FSharp.Control.Asyncmodules et ValueTask les modules Taskexposent ces fonctions qualifiées. M<'T> dans la colonne de forme correspond au type de calcul correspondant, et non à une abréviation de type F#.

Fonctions qualifiées Forme
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>; argument de type d’entrée explicite requis.
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>

Par exemple, mappez le contenu d’un fichier à une longueur, puis représentez un échec de lecture en tant que Result:

open System.IO
open System.Threading

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

Un fichier contenant Report 1 des données .Ok 8 Un fichier manquant donne un Error fichier contenant un FileNotFoundException. Pour ignorer une valeur réussie au lieu de la transformer, spécifiez le type d’entrée lorsque vous appelez ignore:

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

catchWith fournit une valeur de remplacement pour une défaillance sans annulation ; il n’exécute pas un autre calcul asynchrone. catch renvoie Ok ou Error. L’annulation n’est pas convertie en valeur d’erreur ou transmise au gestionnaire de récupération. Task.empty est un Task<unit>, pas le non générique Task.CompletedTask.

Contrairement aux minuscules, les retours Async.catchChoiceexistants Async.Catch . La récupération gère les échecs du calcul fourni, et non les exceptions synchrones levées avant l’appel. Async reste différé ; les tâches restent impatientes.

Disponibilité de ValueTask et de bibliothèque

Les API ValueTask nécessitent la génération ou net10.0 la netstandard2.1 génération de FSharp.Core 11. Consultez la sélection cible principale.

Observez le contrat de consommation unique ValueTask : consommez chaque valeur une seule fois, et non par le biais d’attentes ou de pipelines distincts. Pour les attentes répétées, appelez Task.ofValueTask une seule fois et réutilisez uniquement la tâche résultante.

Voir aussi