Exemple d’un composant pour calculer Pi

Cet exemple présente un composant qui calcule la valeur de Pi en utilisant la formule de Leibniz-Gregory et se met à jour à chaque étape du calcul.

Rendez-vous en bas de la page, le calcul a déjà commencé !

Description du composant

Le composant est une fonction nommée LeibnizGregory attendant une propriété step et retournant un élément MathML <math>.

La propriété step doit être une donnée observable d’un objet littéral contenant 2 propriétés :

  1. pi : un nombre qui contient la valeur estimé de Pi à l’étape numéro n du calcul,
  2. n : un entier qui est le numéro de l’étape.

La propriété step est requise et représente une étape du calcul. À la première étape, les valeurs des propriétés pi et n de step doivent être à zéro. Le composant met à jour la propriété step à chaque étape et il est possible de reprendre le calcul à n’importe quelle étape en redonnant la dernière valeur de step.

Code du composant

Le code du composant est représenté ici.

function LeibnizGregory({step}) {
const {
math, mi, mo, munderover, mrow, mn, mfrac, msup
} = Room.elements("http://www.w3.org/1998/Math/MathML");

let id = 0;
const calculation = () => {
step.pi += 4 * Math.pow(-1, step.n) / (2 * step.n + 1);
step.n++;
(step.pi == Math.PI) && stop();
};
const start = () => !id && (id = setInterval(calculation, 10));
const stop = () => (clearInterval(id), id = 0);

return math({onMount: start, onUnmount: stop},
munderover(
mrow(mo("∑")),
mrow(mi("n"), mo("="), mn("0")),
mrow(mn(() => step.n))
),
mfrac(
mrow(
mn("4"), mo("×"),
msup(
mrow(mrow(mo("("), mo("-"), mn("1"), mo(")"))),
mrow(mi("n"))
)
),
mrow(mn("2"), mi("n"), mo("+"), mn("1"))
),
mo("="),
mn(() => step.pi)
);
}

Le composant retourne un élément MathML <math> ressemblant à l’exemple de Composition avec MathML mais où, à l’étape en cours, le symbole infini est remplacé par la valeur de la propriété n et la valeur de la propriété pi est placée après la représentation de la formule.

La fonction start() est attribuée à l’évènement mount pour l’élément <math> et la fonction stop() à l’évènement unmount.

Donc à l’ajout du composant dans le DOM, la fonction start() est appelée et met en place un timer (utilisation de la fonction setInterval()) qui va se répéter et exécuter une étape du calcul (fonction calculation()) toutes les 10 millisecondes.

Inversement, quand le composant est supprimé du DOM, la fonction stop() est appelée et supprime le timer, ce qui arrête le calcul.

Le calcul s’arrête également si la valeur de la propriété pi est égale à la valeur donnée par la constante Math.PI. Ceci est fait dans la fonction calculation() par un appel à la fonction stop().

Notez ici l’importance de l’utilisation du unmount, sans cela, le timer mis en place n’est jamais supprimé, même si le composant est supprimé du DOM de la page.

L’utilisation des fonctions start() et stop() pour les évènements pageShow et pageHide était aussi envisageable mais pas indispensable, les timers sont en général suspendus par les navigateurs quand une page n’est plus visible (changement d’onglet par exemple).

Utilisation du composant

Pour utiliser ce composant, une donnée observable step doit être créée avec la fonction createData() de Room et passée au composant. Afin de conserver les calculs, la valeur de cette donnée est enregistrée dans le localStorage.

Pour lire la donnée step, une fonction load() est attribuée à l’élément <p> (qui contient le composant) pour l’évènement mount. La donnée est ainsi chargée dés que l’élément <p> est ajouté au DOM de la page.

Pour enregistrer la donnée step, une fonction save() est attribuée à l’élément <p> contenant le composant pour les évènements unmount et pageHide. La donnée est ainsi sauvegardée quand le composant est supprimé du DOM ou en cas de rechargement de la page.

Par ailleurs, un élément <p> est également ajouté pour montrer la valeur de la constante Math.PI.

Ceci donne les lignes de code suivantes :

const {p} = Room.elements();
const {math, mi, mo, mn} = Room.elements("http://www.w3.org/1998/Math/MathML");
const step = Room.createData({pi: 0, n: 0});
const name = "CalculPi";
try {
Room.setData(step, JSON.parse(localStorage.getItem(name)) || step);
} catch(e) {}
const save = () => {
try {
localStorage.setItem(name, JSON.stringify(Room.getData(step)));
} catch(e) {}
};
element.append(
p(math(mi("π"), mo("≈"), mn(Math.PI))),
p({onUnMount: save, onPageHide: save}, LeibnizGregory({step}))
);

Et le résultat est le suivant :

Il est notable que ce n’est pas avec cette formule que l’on peut calculer des centaines de milliards de décimales de Pi, elle converge bien trop lentement !

Dernière mise à jour :