gestion_table_tournante_libre/src/application/executeur_travailleur.js

98 lines
3.8 KiB
JavaScript
Raw Normal View History

// © 2026 TechnoLibre (http://www.technolibre.ca)
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
// L'exécuteur de production de la génération (§ 5.10, § 13.4) : la
// recherche tourne dans un travailleur, hors du fil de l'interface, et
// l'exécuteur adapte son protocole — celui de generation_travailleur.js —
// au contrat Executeur du contrôleur de génération (generation.js).
//
// → { type: 'lancer', configuration, reglages } le premier et seul message
// ← { type: 'avancement', fait, total } remis aux abonnés
// ← { type: 'resultat', propositions } résout le résultat
// ← { type: 'erreur', code, details } rejette par ErreurGeneration
//
// Chaque lancer prend un travailleur neuf de la fabrique et le termine à
// l'issue : résultat, erreur, faute du travailleur ou annulation. Terminé,
// le travailleur ne remet plus rien : un message déjà en file à cet instant
// ne parvient ni aux abonnés ni au résultat. L'annulation est terminate(),
// dans le même tour : immédiate, sans résultat partiel. La fabrique est un
// paramètre : src/main.js la construit sur le travailleur inclus dans la
// page, une épreuve sur un travailleur factice. Ce module ne touche aucune
// plateforme.
import { ErreurAnnulee, ErreurGeneration } from './generation.js';
/**
* L'exécuteur du travailleur.
*
* @param {() => {postMessage: (message: Object) => void, terminate: () => void,
* onmessage: ((evenement: {data: Object}) => void)|null,
* onerror?: ((evenement: {message?: string}) => void)|null}} fabrique
* rend un travailleur neuf à chaque appel
* @returns {import('./generation.js').Executeur}
* lancer(configuration, reglages) rend une Execution : avancement →
* surAvancement ; resultat → la promesse résolue aux propositions reçues,
* puis terminate() ; erreur {code, details} → rejet par
* ErreurGeneration(code, details) ; faute du travailleur, son canal
* d'erreur, → rejet par une Error sans code, qui remonte comme une faute ;
* annuler() → terminate() aussitôt, rejet par ErreurAnnulee, plus aucun
* avancement. Après l'issue, annuler() ne fait rien.
*/
export function creerExecuteurTravailleur(fabrique) {
return {
lancer(configuration, reglages) {
const travailleur = fabrique();
const abonnes = new Set();
let termine = false;
let resoudre;
let rejeter;
const resultat = new Promise((ok, ko) => {
resoudre = ok;
rejeter = ko;
});
// L'issue : le travailleur se termine, ses gestionnaires sont retirés,
// et rien de ce qu'il remettrait encore ne passe.
function terminer() {
termine = true;
travailleur.onmessage = null;
travailleur.onerror = null;
travailleur.terminate();
}
travailleur.onmessage = ({ data }) => {
if (termine) return;
if (data.type === 'avancement') {
for (const fn of [...abonnes]) fn(data.fait, data.total);
} else if (data.type === 'resultat') {
resoudre(data.propositions);
terminer();
} else if (data.type === 'erreur') {
rejeter(new ErreurGeneration(data.code, data.details));
terminer();
}
};
travailleur.onerror = (evenement) => {
if (termine) return;
rejeter(new Error(`travailleur de génération : ${evenement?.message ?? 'faute sans message'}`));
terminer();
};
travailleur.postMessage({ type: 'lancer', configuration, reglages });
return {
surAvancement(fn) {
abonnes.add(fn);
return () => {
abonnes.delete(fn);
};
},
resultat,
annuler() {
if (termine) return;
terminer();
rejeter(new ErreurAnnulee());
},
};
},
};
}