Les Tasks dans MOGWAI

Les timers permettent d’exécuter du code à intervalles réguliers, les événements de réagir à des déclenchements ponctuels — mais que faire quand on a besoin d’exécuter un traitement long en parallèle du programme principal, avec un vrai suivi de son état, un passage de paramètres structuré et une gestion robuste des erreurs ? C’est là qu’interviennent les Tasks.

Le modèle parent/enfant

Une Task enfant est un bloc de code qui s’exécute en parallèle de la Task parente qui l’a lancée. La Task parente peut continuer son propre travail pendant que ses enfants s’exécutent.

Comme les timers et les événements, une Task enfant possède sa propre stack, isolée de celle de la Task parente. En revanche, contrairement aux timers, les Tasks ne partagent pas les variables globales — elles communiquent exclusivement via des événements système dédiés.

Une Task enfant peut elle-même créer des Tasks enfants. Il n’y a pas de limite autre que la mémoire disponible, mais il est recommandé de ne pas dépasser 50 à 100 Tasks simultanées.

Déclarer une Task

La syntaxe de déclaration est proche de celle des timers :

task 'T1' do
{
    # Code de la Task enfant

    "Je m'exécute en parallèle !" ?
    true task.setResult
}

La déclaration ne lance pas la Task — elle la définit. C’est task...start...with qui la démarre.

Lancer et contrôler une Task

Le parent dispose des fonctions suivantes :

  • task 'T1' start with objet — lance la Task en lui passant un paramètre
  • 'T1' task.stop — arrête la Task
  • 'T1' task.wait — attend que la Task se termine avant de continuer
  • ('T1' 'T2' 'T3') task.join — attend que toutes les Tasks listées soient terminées
  • 'T1' task.isRunning — retourne true si la Task est en cours d’exécution
  • 'T1' task.result — retourne le résultat défini par la Task via task.setResult
  • 'T1' task.purge — supprime la Task (l’arrête si elle tourne encore)
  • task.list — retourne la liste de toutes les Tasks déclarées

Passer des paramètres à une Task enfant

Au lancement, la Task parente peut transmettre n’importe quel objet MOGWAI à la Task enfant via start with. Cet objet est automatiquement placé sur la stack de l’enfant au démarrage.

En pratique, on utilise souvent un record pour passer plusieurs valeurs en une fois, combiné à ->vars pour les éclater en variables locales :

task 'T1' do
{
    # Le paramètre est sur la stack au démarrage

    ->vars

    # Les variables locales 'url' et 'filename' sont maintenant disponibles

    "Téléchargement de {! url}" eval ?
}

task 'T1' start with [url: "https://example.com" filename: "page.bin"]

Attention : lancer une Task déjà en cours d’exécution provoque une erreur. Il est recommandé de vérifier avec task.isRunning avant de la relancer.

Communication parent ↔ enfant

La communication entre Tasks repose entièrement sur des événements système que MOGWAI déclenche automatiquement. Le parent déclare les gestionnaires qui l’intéressent avec onEvent.

Événements déclenchés par l’enfant vers le parent

ÉvénementContenu de eventData
TASK_DID_STARTLe nom de la Task (ex. 'T1')
TASK_DID_ENDUn record [task: 'T1' result: ...]
TASK_DID_FAILUn record [task: 'T1' error: ... message: "..."]
TASK_DID_PUBLISHUn record [task: 'T1' message: ...]

Événement déclenché par le parent vers l’enfant

ÉvénementContenu de eventData
TASK_DID_RECEIVEL’objet envoyé par le parent via task send

Fonctions de communication côté enfant

  • objet task.publish — envoie un message au parent via TASK_DID_PUBLISH
  • objet task.setResult — définit le résultat final de la Task, récupérable par le parent via task.result
  • task.name — retourne le nom de la Task courante

Envoyer un message à une Task enfant depuis le parent

task 'T1' send "message pour T1"

L’enfant reçoit ce message via l’événement TASK_DID_RECEIVE.

Gestion des erreurs

Si une Task enfant lève une erreur non gérée, elle s’arrête automatiquement et l’événement TASK_DID_FAIL est déclenché dans la Task parente avec le code et le message d’erreur.

Il est donc fortement recommandé d’utiliser guard dans le code des Tasks enfants pour capturer les erreurs et retourner un résultat explicite :

task 'T1' do
{
    ->vars

    guard
    {
        # code pouvant lever une erreur

        true task.setResult
    }
    else
    {
        false task.setResult
    }
}

Exemple complet

Voici un programme qui télécharge trois pages web en parallèle et les sauvegarde sur disque. Chaque téléchargement est confié à une Task enfant dédiée.

mogwai.reset
console.clear

# Déclaration des événements de suivi des Tasks

onEvent 'TASK_DID_START' do
{
    "TASK DID START {! eventData}" eval ?
}

onEvent 'TASK_DID_END' do
{
    "TASK DID END {! eventData}" eval ?
}

onEvent 'TASK_DID_FAIL' do
{
    "TASK DID FAIL {! eventData}" eval ?
}

onEvent 'TASK_DID_PUBLISH' do
{
    "TASK DID PUBLISH {! eventData}" eval ?
}

# Préparation des paramètres pour chaque Task

(
    [name: 'T1' url: "https://www.google.fr"        filename: "google.bin"]
    [name: 'T2' url: "https://www.coding4phone.com" filename: "c4p.bin"]
    [name: 'T3' url: "https://www.mogwai.eu.com"    filename: "mogwai.bin"]
)

foreach 'item' do
{
    item ->vars

    task name do
    {
        # Le paramètre passé par start with est sur la stack

        ->vars

        now -> 'begin'

        [! http.get uri: url] -> 'r'

        now begin - ->duration -> 'd'

        if (r->state) then
        {
            "Durée du téléchargement : ({! d->ms} ms)" eval task.publish

            guard
            {
                filename r->response file.data.write
                true task.setResult
            }
            else
            {
                false task.setResult
            }
        }
        else
        {
            "Erreur de téléchargement !" task.publish
            false task.setResult
        }
    }

    task name start with [url: url filename: filename]
}

# On attend que les trois Tasks soient terminées

('T1' 'T2' 'T3') task.join

"PROGRAMME TERMINÉ" ?

La sortie console ressemblera à ceci :

TASK DID START 'T3'
TASK DID START 'T2'
TASK DID START 'T1'
TASK DID PUBLISH [task: 'T2' message: "Durée du téléchargement : (475 ms)"]
TASK DID END [task: 'T2' result: true]
TASK DID PUBLISH [task: 'T1' message: "Durée du téléchargement : (579 ms)"]
TASK DID END [task: 'T1' result: true]
TASK DID PUBLISH [task: 'T3' message: "Durée du téléchargement : (807 ms)"]
TASK DID END [task: 'T3' result: true]
PROGRAMME TERMINÉ

Les trois téléchargements se lancent quasi simultanément et se terminent dans l’ordre de leur durée respective. task.join garantit que le programme n’affiche « PROGRAMME TERMINÉ » qu’une fois tous les enfants terminés.

Bonnes pratiques

  • Toujours utiliser guard dans les Tasks enfants pour capturer les erreurs.
  • Utiliser task.setResult pour retourner un statut de succès ou d’échec au parent.
  • Préférer task.join aux boucles d’attente avec task.isRunning.
  • Ne pas dépasser 50 à 100 Tasks simultanées pour ne pas saturer la mémoire.

Tasks, timers ou événements ?

Les trois mécanismes sont complémentaires. Les timers sont idéaux pour les actions périodiques légères. Les événements conviennent pour réagir à des déclenchements ponctuels et découpler les composants du programme. Les Tasks s’imposent dès qu’on a besoin d’exécuter un traitement long en parallèle, avec un passage de paramètres structuré, un suivi de cycle de vie et une gestion d’erreurs robuste.

Laisser un commentaire

En savoir plus sur CODING 4 PHONE

Abonnez-vous pour poursuivre la lecture et avoir accès à l’ensemble des archives.

Poursuivre la lecture

En savoir plus sur CODING 4 PHONE

Abonnez-vous pour poursuivre la lecture et avoir accès à l’ensemble des archives.

Poursuivre la lecture