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— retournetruesi la Task est en cours d’exécution'T1' task.result— retourne le résultat défini par la Task viatask.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énement | Contenu de eventData |
|---|---|
TASK_DID_START | Le nom de la Task (ex. 'T1') |
TASK_DID_END | Un record [task: 'T1' result: ...] |
TASK_DID_FAIL | Un record [task: 'T1' error: ... message: "..."] |
TASK_DID_PUBLISH | Un record [task: 'T1' message: ...] |
Événement déclenché par le parent vers l’enfant
| Événement | Contenu de eventData |
|---|---|
TASK_DID_RECEIVE | L’objet envoyé par le parent via task send |
Fonctions de communication côté enfant
objet task.publish— envoie un message au parent viaTASK_DID_PUBLISHobjet task.setResult— définit le résultat final de la Task, récupérable par le parent viatask.resulttask.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
guarddans les Tasks enfants pour capturer les erreurs. - Utiliser
task.setResultpour retourner un statut de succès ou d’échec au parent. - Préférer
task.joinaux boucles d’attente avectask.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.