Workflow asynchrone

Besoins

  1. Ne pas bloquer le thread courant en attendant un calcul long

  2. Permettre calculs en parallèle

  3. Indiquer qu'un calcul peut prendre du temps

Type Async<'T>

  • Représente un calcul asynchrone

  • Similaire au pattern async/await avant l'heure 📆

    • 2007 : Async<'T> F♯

    • 2012 : Task<T> .NET et pattern async/await

    • 2017 : Promise JavaScript et pattern async/await

Méthodes renvoyant un objet Async

Async.AwaitTask(task: Task or Task<'T>) : Async<'T>

  • Conversion d'une Task (.NET) en Async (F♯)

Async.Sleep(milliseconds or TimeSpan) : Async<unit>

  • await Task.Delay()Thread.Sleep → ne bloque pas le thread courant

FSharp.Control.CommonExtensions : étend le type System.IO.Stream

  • AsyncRead(buffer: byte[], ?offset: int, ?count: int) : Async<int>

  • AsyncWrite(buffer: byte[], ?offset: int, ?count: int) : Async<unit>

FSharp.Control.WebExtensions : étend le type System.Net.WebClient

  • AsyncDownloadData(address: Uri) : Async<byte[]>

  • AsyncDownloadString(address: Uri) : Async<string

Lancement d'un calcul async

Async.RunSynchronously(calc: Async<'T>, ?timeoutMs: int, ?cancellationToken) : 'T → Attend la fin du calcul mais bloque le thread appelant ! (≠ await C♯) ⚠️

Async.Start(operation: Async<unit>, ?cancellationToken) : unit → Exécute l'opération en background (sans bloqué le thread appelant) ⚠️ Si une exception survient, elle est "avalée" !

Async.StartImmediate(calc: Async<'T>, ?cancellationToken) : unit → Exécute le calcul dans le thread appelant ! 💡 Pratique dans une GUI pour la mettre à jour : barre de progression...

Async.StartWithContinuations(calc, continuations..., ?cancellationToken) → Idem Async.RunSynchronously ⚠️ ... avec 3 callbacks de continuation : en cas de succès ✅, d'exception 💥 et d'annulation 🛑

Bloc async { expression }

A.k.a. Async workflow

Syntaxe pour écrire de manière séquentielle un calcul asynchrone → Le résultat du calcul est wrappé dans un objet Async

Mots clés

  • return → valeur finale du calcul - unit si omis

  • let! (prononcer « let bang ») → accès au résultat d'un sous-calcul async (≃ await en C♯)

  • use! → idem use (gestion d'un IDisposable) + let!

  • do! → idem let! pour calcul async sans retour (Async<unit>)

Usage inapproprié de Async.RunSynchronously

Async.RunSynchronously lance le calcul et renvoie son résultat MAIS en bloquant le thread appelant ! Ne l'utiliser qu'en « bout de chaîne » et pas pour unwrap des calculs asynchrones intermédiaires ! Utiliser plutôt un bloc async.

Calculs en parallèle

Async.Parallel

Async.Parallel(computations: seq<Async<'T>>, ?maxBranches) : Async<'T[]>

Task.WhenAll : modèle Fork-Join

  • Fork : calculs lancés en parallèle

  • Attente de la terminaison de tous les calculs

  • Join : agrégation des résultats (qui sont du même type)

    • dans le même ordre que les calculs

Async.StartChild

Async.StartChild(calc: Async<'T>, ?timeoutMs: int) : Async<Async<'T>>

Permet de lancer en parallèle plusieurs calculs → ... dont les résultats sont de types différents (≠ Async.Parallel)

S'utilise dans bloc async avec 2 let! par calcul enfant (cf. Async<Async<'T>>)

Annulation conjointe 📍 Annulation d'une tâche → Le calcul enfant partage le jeton d’annulation du calcul parent

Exemple :

Soit le fonction delay → qui renvoie la valeur spécifiée x → au bout de ms millisecondes

💡 Minutage avec la directive FSI #time (doc)

Annulation d'une tâche

Se base sur un CancellationToken/Source par défaut ou explicite :

  • Async.RunSynchronously(computation, ?timeout, ?cancellationToken)

  • Async.Start(computation, ?cancellationToken)

Déclencher l'annulation

  • Token explicite + cancellationTokenSource.Cancel()

  • Token explicite avec timeout new CancellationTokenSource(timeout)

  • Token par défaut : Async.CancelDefaultToken()OperationCanceledException💥

Vérifier l'annulation

  • Implicite : à chaque mot clé dans bloc async : let, let!, for...

  • Explicite local : let! ct = Async.CancellationToken puis ct.IsCancellationRequested

  • Explicite global : Async.OnCancel(callback)

Exemple :

Résultat :

Mis à jour

Ce contenu vous a-t-il été utile ?