Maison > interface Web > js tutoriel > Chunk-Busters : Ne traversez pas les ruisseaux !

Chunk-Busters : Ne traversez pas les ruisseaux !

Linda Hamilton
Libérer: 2024-12-02 05:00:10
original
854 Les gens l'ont consulté

⚠️ Si vous souffrez de photosensibilité, vous voudrez probablement ignorer cela.
Voir l'image statique ci-dessous, ces lumières commenceront à clignoter très rapidement !

Chunk-Busters: Don’t cross the Streams!

Comment fonctionne Internet ?

Souvenez-vous du titre… nous parlons ici de streams.

Je pourrais parler de protocoles, de paquets, d'ordre, d'acks et de nacks… mais nous parlons ici de flux, et comme vous l'avez probablement deviné (je crois en vous =D) avec les flux… c'est soit binaire, soit chaînes.

Oui, les chaînes sont compressées avant d'être envoyées… mais pour ce qui nous importe habituellement lors du développement front et backend… les chaînes et les binaires.

Dans les exemples suivants, j'utiliserai des flux JS.

Bien que Node ait ses propres implémentations héritées, nous avons des moyens de gérer les flux qui contiennent le même code, que ce soit au début ou à la fin.

D'autres langages ont leur façon de gérer les flux, mais comme vous le verrez… la partie code proprement dite pour gérer cela n'était pas si compliquée (pour ne pas dire qu'il ne se passe pas de choses complexes).

L'exemple de problème

Vous disposez d'une interface qui doit consommer des données provenant de plusieurs sources.

Bien que vous puissiez accéder à chaque source individuellement via son IP/port, vous les placez derrière une passerelle API pour faciliter l'utilisation et le contrôle.

Le dépôt

Vérifiez le dépôt sur le lien, apprenez-y à l'exécuter vous-même pour pouvoir jouer avec.

https://github.com/Noriller/chunk-busters

La vidéo

Version vidéo à suivre :

https://youtu.be/QucaOfFI0fM

v0 - l'implémentation naïve

Vous avez les sources, vous récupérez, attendez et effectuez le rendu. Rincez et répétez.

await fetch1();
handleResult(1);
await fetch2();
handleResult(2);
...
await fetch9();
handleResult(9);
Copier après la connexion
Copier après la connexion

Vous pensez peut-être que personne ne fera réellement ça…

Dans cet exemple, il est clair que quelque chose ne va pas, mais ce n'est pas si difficile de tomber dans ce piège.

L’évidence : c’est lent. Vous devez tirer et attendre chaque demande et si c'est lent… vous devez attendre.

v1 - la version impatiente

Vous savez que vous ne voulez pas attendre chaque demande individuellement… alors vous lancez toutes et attendez qu'elles se terminent.

await Promise.all([
  fetch1(),
  fetch2(),
  ...
  fetch9(),
]);
handleAllResults(results);
Copier après la connexion
Copier après la connexion

C'est ce que vous faites probablement, alors c'est bien, non ?

Je veux dire, sauf si vous avez UNE seule requête qui est lente… cela signifierait que même si toutes les autres sont déjà terminées… vous devrez quand même attendre que celle-là se termine.

v2 - la version la plus intelligente et la plus enthousiaste

Vous savez que certaines requêtes peuvent être plus lentes, donc vous lancez toujours toutes et attendez, mais au fur et à mesure qu'elles arrivent, vous faites déjà quelque chose avec le résultat lorsque cela est possible, donc quand la dernière arrive, les autres sont déjà terminées.

await fetch1();
handleResult(1);
await fetch2();
handleResult(2);
...
await fetch9();
handleResult(9);
Copier après la connexion
Copier après la connexion

Cela DOIT être la meilleure solution, n'est-ce pas ?

Hmm… quelque chose de bizarre ?

v3 - Je t'ai menti… voici à quoi devrait ressembler la v1

Vous vous souvenez de la v1 ? Ouais… voilà à quoi ça devrait ressembler :

Il s'avère qu'il y a une limite au nombre de connexions que vous pouvez avoir avec exactement le même point de terminaison dans http/1 et pas seulement… cela dépend du navigateur et chaque navigateur peut avoir des limites différentes.

Vous pourriez penser à simplement utiliser http/2 et mettre un terme à cela… mais même si c'était une bonne solution, vous devez toujours gérer plusieurs points de terminaison dans le frontend.

Existe-t-il au moins une bonne solution pour cela ?

v4 - entrez dans les flux !

Revoyons la v0 mais en utilisant des flux…

Vous êtes intelligent, donc vous vous attendiez probablement à cela puisque l'avertissement l'a un peu gâché… mais oui… ce que vous voyiez auparavant n'était pas toutes les données générées par le backend.

Quoi qu'il en soit… au fur et à mesure que nous récupérons, nous rendons.

await Promise.all([
  fetch1(),
  fetch2(),
  ...
  fetch9(),
]);
handleAllResults(results);
Copier après la connexion
Copier après la connexion

Si nous tapons plutôt sur le flux à venir, nous pouvons faire quelque chose avec les morceaux de données au fur et à mesure qu'ils arrivent. (Oui ! Aimez Chat GPT et autres.)

Même si la v0 est la pire façon de gérer ce problème, elle est grandement améliorée en utilisant les flux. Vous pouvez tromper l'utilisateur en montrant quelque chose, n'importe quoi, même si le temps d'attente total est le même.

v5 - v1, encore une fois, mais avec des streams !

Le problème http/1 est toujours d'actualité, mais encore une fois, vous pouvez déjà voir les choses telles qu'elles viennent.

Ouais… je ne peux plus retarder ça… alors…

v6 - une API pour les gouverner tous !

Ou… peut-être que je peux ?

Vous voyez, le frontend a dû gérer trop de choses… si nous pouvons décharger cela vers le backend, alors vous pouvez avoir un seul point de terminaison qui gérera toutes les sources.

Cela résout la complexité du frontend et les problèmes http/1.

await Promise.all([
  fetch1().then(handleResult),
  fetch2().then(handleResult),
  ...
  fetch9().then(handleResult),
]);
Copier après la connexion


// usually we do this:
await fetch(...).then((res) => {
  // this json call accumulate all the response
  // that later is returned for you to use
  return res.json()
})
Copier après la connexion

v7 - et enfin… une API, plusieurs sources et streaming.

Nous appelons une API, qui appellera toutes les sources, diffusera les données, les gérera et les transmettra au front qui, à son tour, restituera les données au fur et à mesure qu'elles arrivent.

Le code utilisé pour cela est fondamentalement le même au recto et au verso :

await fetchAll();
handleAllResults(results);
Copier après la connexion

Oui… c'est tout (comme le dit l'exemple le plus basique et le plus simple).

Nous ajoutons la chaîne arrivant dans un tampon, l'analysons, vérifions s'il y a un morceau utilisable, l'utilisons et l'oublions. Cela signifie que vous pourriez recevoir/consommer des To de données… un morceau à la fois, avec peu de RAM.

Je sais à quoi tu penses… et c'est stupide… c'est aussi de la folie…

MOOOOOOOOM je veux des Websockets !

Non chérie, nous avons des websockets à la maison !

Websockets à la maison : la suite ?

v8 - ce n'est stupide que si ça ne marche pas

Vous êtes intelligent, vous pensiez que si la source génère toujours des données… alors peut-être pourrions-nous mettre à jour certaines variables…

De cette façon, vous pouvez conserver la connexion utilisée pour obtenir plus de données ou modifier quelque chose par rapport à ce qu'elle génère.

Oui… je suppose que vous pourriez faire ça… et j'ai fait l'exemple sur votre insistance. =D

Pourtant… c’est une idée stupide, je ne sais pas où/si elle peut être utilisée dans un environnement de production réel. Peut-être que si vous voyagez dans le temps jusqu'à cette phase JS délicate entre MPA et Ajax où vous aviez suffisamment d'interactivité, mais pas assez de connexions au même serveur (certains navigateurs avaient une limite de seulement 2 !), alors peut-être ?

A part ça, aucune idée. Si c’est le cas… faites-le-moi savoir.

Dans l'exemple ci-dessus, attention au panneau central, notamment à la "bordure de progression" : vous pouvez voir que l'on continue de se mettre à jour. Si vous ouvriez l'onglet réseau, vous verriez que la connexion GET n'est jamais fermée avant la fin. Vous verriez également plusieurs autres requêtes qui changent ce que faisait cette connexion, toujours active… tout cela avec vanilla http/1.

Quelle est la prochaine étape ?

Chaîne vs JSON

Cet exemple est le plus basique que j'ai pu faire. J'utilise même des chaînes simples au lieu de JSON car c'est plus facile à analyser.

Pour utiliser JSON, vous devez accumuler la chaîne (nous devons JSON.stringify la réponse du backend pour une raison).

Ensuite, vérifiez où le casser, puis analysez cette valeur ou analysez au fur et à mesure.

Pour le premier, pensez NDJSON : au lieu d'un tableau JSON, vous séparez les objets avec de nouvelles lignes, vous pourrez alors "plus facilement" trouver où casser, puis JSON.parser chacun et utiliser l'objet.

Pour ce dernier, vous analysez au fur et à mesure comme dans : vous savez que vous êtes dans un tableau, maintenant c'est un objet, ok première clé, maintenant c'est la valeur de la clé, clé suivante, sautez ça, clé suivante… et ainsi de suite… ce n'est pas quelque chose de trivial à faire manuellement, mais c'est comme passer de l'attente puis du rendu au rendu pendant que vous attendez, c'est tout… sauf… à une échelle encore plus petite.

Gestion des erreurs

Les gens aiment héberger des exemples, celui-ci, vous devez l'exécuter vous-même… J'espère que la raison pour laquelle vous n'hébergez pas l'exemple quelque part est claire maintenant, mais une autre raison est que nous ne nous attendons à aucune erreur ici, et si vous deviez le faire ajoutez les erreurs réseau par-dessus tout… eh bien…

Les erreurs doivent être gérées, mais elles ajoutent une autre couche de complexité.

Devriez-vous l’utiliser ?

Peut-être… tu peux le dire dépend

Il y a des endroits où le streaming est la réponse, mais dans la plupart des cas… wait json suffit (sans parler de plus facile).

Mais l'apprentissage des flux ouvre des voies pour résoudre certains problèmes, que ce soit au niveau du frontend ou du backend.

Dans le frontend, vous pouvez toujours l'utiliser pour « tromper » l'utilisateur. Au lieu d'afficher des spinners partout, vous pouvez montrer quelque chose au fur et à mesure, puis en montrer davantage au fur et à mesure, même si cela prend un certain temps. Tant que vous n'empêchez pas l'utilisateur d'interagir avec lui… vous pouvez même créer quelque chose de « plus lent » que de simplement montrer aux spinners l'impression que c'est bien plus rapide que tout en fait plus rapide .

Dans le backend, vous pouvez économiser de la RAM puisque vous pouvez simplement analyser chaque bloc de données au fur et à mesure qu'il arrive, que ce soit depuis l'avant, une base de données ou tout autre élément intermédiaire. Gérez les données selon vos besoins et envoyez-les sans avoir à attendre la totalité de la charge utile, ce qui entraînerait une erreur MOO (mémoire insuffisante). Des Go, voire des To, de données… bien sûr, pourquoi pas ?

Sortie

React est-il lent ? Cet exemple d'interface a été réalisé avec React et à part la chose « principale » qui se passe avec toutes les « lumières » clignotantes, il se passe beaucoup d'autres choses.

Oui… si vous allez assez vite, l’exemple ne peut pas suivre et commence à se figer. Mais comme il y a facilement des milliers de rendus par minute… je pense que c'est suffisant pour la plupart des applications.

Et vous pouvez toujours améliorer les performances : pour la « bordure de progression », j'ai utilisé des valeurs différées pour la rendre plus fluide si vous devez en enregistrer dans les rendus… Je pourrais faire cela et d'autres améliorations de performances pour les « lumières » et le titre, mais cela empêcherait simplement les « lumières » de clignoter la plupart du temps (ce qui ne ferait pas une belle démo), et aussi le soulignement « électrique » dans le titre ne serait pas aussi amusant que ça. est.

Dans cet exemple, toutes ces « améliorations » ne seraient pas idéales, mais pour des applications normales... vous pouvez lui faire gérer beaucoup de choses. Et si vous avez besoin de quelque chose de plus, utilisez dans ce cas une autre solution.

Conclusion

Ajoutez des streams à votre arsenal… ce n'est peut-être pas une solution panacée, mais cela vous sera sûrement utile un jour.

Et si vous voulez faire quelque chose avec et que vous voulez de l’aide, eh bien… appelez-moi peut-être. =P

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal