Une amie développe un dashboard contenant des graphiques
Malgré une configuration adéquate, son graphique refuse de s'afficher.
A partir de là 10000 questions peuvent se posent sur "pourquoi" ?
Allez hop c'est parti pour une session de 1h de mentorat.
En général le point de départ c'est de partir...du début.
Ouais ok sans blague merci pour cette belle tautologie.
Commencons par le "trivial", on peut déjà écarter un paquet d'hypothèses d'un revers de manche.
D'abord on questionne la dispo de la ressource
Ensuite on questionne la configuration de la ressource
Dans cet exemple de code, on distingue tout de suite un vrai problème...
myChart peut (ou pas du tout) être disponible au moment de son instanciation.
Extrait d'un petit dialogue intérieur :
- Comment ca il est pas dispo ? ben si ya "new Chart" donc c'est bon.
- pas forcément : c'est pas parce que il est instancié qu'il ...existe
- ben si il existe : la preuvre, ya son script d'import en haut.
- ok mais tu touches bien là toute la problématique : entre le moment où Chart est importé et disponible dans le DOM (via window.Chart), et le moment où il est instancié, c'est pas dit que les 2 soit bien synchro.
"Synchro" ha ba tiens ca me fait une super transition pour expliquer le fond du problème.
Ici en effet le code ne se soucie pas de savoir si Chart est dispo. Il considère qu'il l'est. Point.
Et ben non :
si Chart avait été importé dans le bloc header la page web, OK ca fonctionnerait comme ca:
1. d'abord je charge les scripts (les CSS, les meta...) du header
2. ensuite seulement j'interprête le body.
...
Mais dans le code ci-dessus, l'import n'est pas placé dans le bloc header, il est placé et executé au beau milieu de la page, donc il est asynchrone.
Le code lui n'attend pas l'import asynchrone. Donc suivant la vivacité du réseau, y a de fortes chances que code et lib se croisent sans se capter. Des fois oui des fois non. Aléatoire.
(tout ce qu'on déteste)
"Ah oui c'est comme une course de relais : si j'attend pas que mon binôme arrive et me passe le relai en main, je vais pas aller bien loin".
Voila une belle analogie dis-donc pour expliquer la notion de couplage et dépendance.
C'est vrai ? ah ben ca fait plaisir merci
De rien, bref...
Bon c'est bien joli tout ca mais une question me brûle les lèvres : comment on attend ??
En asynchrone ya 2 manières d'attendre, et donc 2 écoles :
- probabiliste VS déterminisite
Probabiliste
-> c'est la méthode dite du doigt mouillé :
exemple typique :
"ba on va se dire que ca va probablement pas prendre plus d'une seconde pour s'importer, au-dela ca serait vraiment abusé.....mais espérons que ca abuse pas"
dans les fait tu mets un setTimeout de 1000ms "comme ca t'es large" et hop tu executes la fonction qui setup le Chart. Ca devrait passer crème (...ou pas).
De mon point de vue c'est la méthode à la fois bien foireuse, non fiable et très présomptueuse.
C'est cette illusion envers soi-même de maitriser son environnement alors que -> que dalle
(pour plein d'autres usecases l'ami setTimeout c'est ok hein...mais pas là)
Déterministe
"je ne vais pas pseudo-décider que l'import va prendre X secondes , car soyons honnêtes j'en sais foutre rien, je suis incapable de contrôler cette latence car ca dépend du CDN et donc d'un serveur distant, du réseau, c'est lent, instable, et imprévisible. Je ne peux pas garantir ce que je ne peux pas contrôler. Fort de ce constat, j'ai besoin qu'un tiers (en l'occurence pas moi) soit en mesure de garantir quand l'import sera fait. Et aussi quand il ne sera pas possible (CDN indispo, ...)"
C'est la méthode où j'encadre l'incertitude.
Alors je sais pas vous mais moi je vais suivre la méthode déterministe :D
Et ben figurez que dans l'API standard Javascript ya une méthode aussi méconnue que géniale qui fait exactement ce job : import()
Elle fait quoi ? pareil que < script src > mais programmatique.
- de manière asynchrone via une bonne vieille Promise :
- elle importe la lib via l'URL qu'on lui donne
- on lui colle un callback dans sa fonction chainée then() (si Promesse tenue -> exec callback)
- un callback dans la fonction chainée catch() pour gérer si ca aboutira pas (si Promesse non tenue/tenable)
Voila c'est ce then() de import(url).then() qui à la fois force l'attente ET qui va garantir que la lib Charts est disponible.
Et ca veut dire que le callback du then() ne sera executée que si la Promesse d'import est tenue. Donc la fonction de setup de Chart (= le callbackà) ne sera executée que si Chart est bien importé.
OK ton exemple résoud nickel la problématique pour 1 charts.
Mais si j'ai au moins 2 Charts, je fais comment parce que là je risque de re-importer une lib qui a déjà été importée auparavant. J'ai pas envie de faire ça mais je sais pas comment faire autrement.
Alors j'ai déjà donné un indice subliminal dans mon explication ci-dessus :
lorsque la lib est importée, elle devient disponible dans window.Chart.
Si elle est pas dispo, la prop est undefined. Simple.
et window c'est l'objet racine global de la page, qui contient tout.
Donc tu peux faire comme ca :
if (window.Chart === undefined){
// import + setup
import(url).then(initChart);
}
else {
// setup direct
initChart()
}
ou encore en monde inlined :
window.Chart && initChart()
et au final on aboutit toujours à initChat.
Et en plus on duplique même pas accidentellement le code de setup du Chart.
Et ca c'est cool.
Par contre garde en tête que l'import dynamique est à considérer comme une solution "on-the-fly".
Autrement dit, si t'en a besoin en one-shot c'est bon, mais au dela c'est pas une bonne pratique,
car dans l'exemple que je te donne, on trimbale à la fois la gestion l'import ET l'execution.
Or dans un usage "classique" la lib est déjà dispo en global, on gère QUE le setup du Chart.
Ah ben justement si je pousse le bouchon : je veux non pas 2 mais 3 charts.
Ok là tu commences à industrialiser l'usage des Charts.
il est temps de rendre l'import et la disponibilité de Chart de manière globale au site ou à la page sans recourir à l'import dynamique avec import().
Il faut mettre une balise statique dans le bloc header. Statique par oppositon à dynamique : en HTML. Dispo à 1 endroit, instancié N fois.
Voila la résolution finale :