11521 sujets

JavaScript, DOM et API Web HTML5

Bonsoir
Depuis de nombreuses années, j'utilise XMLHttpRequest pour charger des données depuis le serveur.
En examinant les warnings, je trouve la point suivant:
L’utilisation d’XMLHttpRequest de façon synchrone sur le fil d’exécution principal est obsolète à cause de son impact négatif sur la navigation de l’utilisateur final. Consulter  http://xhr.spec.whatwg.org/  pour plus d’informations.

J'ai consulté la page en question, qui dit bien que c'est obsolète mais ne donne pas clairement de solution pour cela.
Doit on utiliser la même fonction de façon asynchrone?
Cela me semble peu convenir à mon cas d'utilisation.
Y a-t-il une autre façon d'obtenir le même résultat?

Merci de vos conseils
Hello,

Selon moi, la solution est donnée de manière un peu implicite. Il faudrait effectivement se contenter d'une requête asynchrone. Mais au juste, pour quelle raison une telle requête ne te convient pas?
Anymah a écrit :
Mais au juste, pour quelle raison une telle requête ne te convient pas?

Ah ben la question m'intéresse aussi tiens : étant donné que je pensais que le mode asynchrone était justement tout l'intérêt d'utiliser Ajax.

En jQuery les configurations pour Ajax sont asynchrones par défaut.
Bonjour.

J'ai toujours utilisé XMLHttpRequest de façon asynchrone... à telle enseigne que je ne vois pas vraiment l'intérêt d'une utilisation synchrone.

D'après cette page, l'interface utilisateur est bloquée le temps que les données soient rapatriées... Ce doit être assez frustrant pour l'utilisateur.

Smiley smile
Zelena a écrit :
Bonjour.
J'ai toujours utilisé XMLHttpRequest de façon asynchrone... à telle enseigne que je ne vois pas vraiment l'intérêt d'une utilisation synchrone.
Smiley smile

J'ai toujours utilisé XMLHttpRequest de façon synchrone... à telle enseigne que je ne vois pas vraiment l'intérêt d'une utilisation asynchrone.... Smiley smile
Bon, je vais essayer!
Zelena a écrit :
Bonjour.
D'après cette page, l'interface utilisateur est bloquée le temps que les données soient rapatriées... Ce doit être assez frustrant pour l'utilisateur.

Tout dépend de ce qu'il y a dans la page, et des interactions que l'utilisateur est sensé effectuer.
Dans mon cas, il s'agit d'afficher des données, pas de jouer à un jeu vidéo. En fait, il n'y a rien que l'utilisateur puisse voir d'intéressant sur la page tant que la requête n'a pas été effectuée et ses résultats affichés. Ce n'est qu'une fois les résultats affichés que la page montre son contenu.
Administrateur
Hello,
PapyJP a écrit :

J'ai toujours utilisé XMLHttpRequest de façon synchrone... à telle enseigne que je ne vois pas vraiment l'intérêt d'une utilisation asynchrone.... Smiley smile

Pour des raisons de performance : cela évite de bloquer l'affichage de la page.

C'est une "bonne pratique" générale, mais cela dépend de ton usage et de tes besoins en effet.
Modifié par Raphael (30 Dec 2016 - 10:08)
Raphael a écrit :
Hello,

Pour des raisons de performance : cela évite de bloquer l'affichage de la page.

C'est une "bonne pratique" générale, mais cela dépend de ton usage et de tes besoins en effet.

C'est surtout que je ne vois pas ce que pourrait faire mon script tant que le résultat n'est pas chargé.
La requête sert à recevoir le contenu d'une balise: j'appelle un programme PHP en lui donnant une information, il me répond en donnant en HTML le contenu d'une balise <div> (ou autre) quelque chose qui est calculé dynamiquement en fonction du contexte (valeur de l'information fournie, date, contenu du site, etc.).

De plus cet appel se fait par une méthode d'un objet.
Actuellement le code la méthode est
this.load = function() {
    var url = '/get-tilelist.php?lang=' + getLanguage() + '&addrList=' + this.addrList.join(',');
    var div = document.createElement('div');
    div.innerHTML = getRemote(url);
    this.section.appendChild(div);
}
où getRemote(url) est la fonction synchrone qui récupère le résultat de la requête.

Supposons (ce qui est le cas dans certaines de mes pages) que j'aie 3 objets de ce type.
Actuellement, comme tout est synchrone, le chargement se fait de façon synchrone, donc un objet à la fois.
Si je voulais le faire de façon asynchrone, il faudrait passer une fonction de callback qui soit différente pour chacun des objets. Or, autant que j'aie pu comprendre, on ne peut pas passer en callbak une méthode d'un objet: si on met "this.method", le moteur JavaScript comprend "this" comme étant le contexte général de la page.
Il faut donc une fonction de callback qui soit unique dans la page et qui retrouve par je ne sais quel moyen quel est l'objet dont les données sont arrivées et appelle la méthode d'affichage du bon objet... ça me semble assez compliqué à faire, mais je si quelqu'un a des propositions, je suis toujours prêt à essayer, car ce n'est qu'en faisant de tels essais qu'on progresse.
Bonsoir,

A mon avis, il n'y a qu'un seul cas où une requête synchrone est véritablement utile: une requête qui s'exécuterait en quittant la page (évènement unload).
Dans tous les autres cas, l'asynchrone est meilleur.

Ton exemple avec 3 éléments à charger est parfait pour illustrer ce que tu as à gagner en passant à l'asynchrone:
Admettons que la première ressource met 2s à être chargée, la deuxième 3s et la troisième 4s.

En mode synchrone, une seule requête est lancée à la fois, la suivante ne se lance que quand la précédente se termine.
ce qui fait que les trois éléments se chargent en un total de 2+3+4=9s.

En mode asynchrone, les trois requêtes sont lancées en parralèle en même temps.
ce qui fait qu'en 4s, tout est chargé (temps de la requête la plus lente).
N'est-ce pas fantastique ?


Pour ton problème de this, la solution existe et est très simple, elle s'appelle bind.
Au lieu de passer this.method en tant que callback, tu passes this.method.bind(this).
Merci pour ces précisions.
Je suis bien d'accord de l'intérêt de rendre les requêtes asynchrones.
Je vais regarder comment mettre cela en œuvre.
Bonjour à tous et bonne année.

J'ai commencé à modifier mes chargements synchrones pour les rendre asynchrones, ce qui me renvoie au premier programme professionnel que j'ai écrit en 1968.... ça fait un bail!

Le chargement en soi ne pose pas de problème, ce qui est plus enquiquinant, c'est de gérer la synchronisation des retours.

Dans la version synchrone, le programme charge depuis le serveur le contenu des N sections, puis effectue un traitement sur l'ensemble de ces sections, en particulier mettre dans une liste unique et triée le contenu de certaines <ul> qui se trouvent dans ces sections.
J'ai donc modifié le design pour que, lorsqu'une <section> est chargée, le programme aille "regarder" si ses les autres <section> ont également fini d'être chargées.

Ou bien y a-t-il un moyen plus simple de gérer la synchro, bien connu de ceux qui savent...?

Autre problème gênant:
L'affichage de la page se fait une première fois avec les <sections> vides et se réaffiche avec le contenu rempli. Cela me semble très gênant pour l'utilisateur.
Je pense donc que je vais revenir au design "synchrone", sauf si vous avez un moyen à me proposer pour éviter ce problème.
Modifié par PapyJP (01 Jan 2017 - 13:04)
Mais du coup, si tout a besoin d'être synchronisé, pourquoi avoir recours à Ajax en première intention pour afficher la page (et pas seulement pour appeler les nouvelles infos demandées après coup suite à un évènement sur la page) ?
Modifié par Olivier C (01 Jan 2017 - 14:57)
Olivier C a écrit :
Mais du coup, si tout a besoin d'être synchronisé, pourquoi avoir recours à Ajax en première intention pour afficher la page (et pas seulement pour appeler les nouvelles infos demandées après coup suite à un évènement sur la page) ?

Le seul évènement, c'est le chargement de la page (onload).
La page ne bouge pas, son contenu dépend du contexte.
Pourquoi j'ai fait cela comme ça?
Parce que le propriétaire du site n'est pas un technicien.
Pour voir le code HTML de la page, regarder le source de cette page
On y trouve par exemple le code HTML suivant:
<section class="tileList description notframed">
    <h2>Tombes de la vallée des rois (Louxor)</h2>
    <p>
        <!-- liste des documents à indexer -->
        ay/e_ay_pharaon_01.htm,
        horemheb/e_horemheb_pharaon_01.htm,
        ramses1/e_ramses1_01.htm,
        /tombes/pharaons/ramses6/e_ramses6_01.htm,
        montouherkhepeshef_kv19/e_montouherkhepeshef_kv19.htm,
        amenhotep3/e_amenhotep3_01.htm,
        thoutmosis4/e_thoutmosis4_01.htm
      </p>
</section>

Le propriétaire modifie cette liste à loisir, il n'a pas besoin de se préoccuper du reste ou de connaître le PHP. Le HTML basique est déjà quelque chose de difficile à maîtriser pour quelqu'un qui est un égyptologue.
Le script récupère le contenu de <p> et s'en sert pour appeler un programme PHP qui génère le contenu correspondant qui vient se mettre dans la <section> à la suite du <p>.
Il y a bien entendu de multiples façons de faire cela, le chargement synchrone étant l'une des possibilités, mais en aucun cas le chargement asynchrone.
Modifié par PapyJP (01 Jan 2017 - 15:53)
Bonne année, et vive mon 6000ème post !

La remarque d'Olivier revient à ce dont j'avais déjà fait allusion dans un de mes posts précédents.
Pourquoi s'évertuer à utiliser AJAX quand tu pourrais charger directement la page avec son contenu final, avec PHP ou un autre langage côté serveur ?

a écrit :
Le chargement en soi ne pose pas de problème, ce qui est plus enquiquinant, c'est de gérer la synchronisation des retours.


Tu as deux approches pour gérer l'asynchronisme:
1. Soit tu affiches quelque chose dès que c'est chargé
2. Soit tu attends que tout soit chargé avant d'afficher

Même si tu choisis l'approche n°2, comme je l'ai déjà expliqué plus haut, tu gagneras en temps de chargement par rapport à la solution synchrone. Donc si tu persistes dans AJAX pour ce cas, oublie vraiment la version synchrone, elle est mauvaise à tout point de vue (sauf pour sa simplicité de programmation).


L'approche n°2 est peut-être un peu plus compliquée à mettre en place, mais pas tant que ça. Sur les navigateurs récents, tu peux utiliser les promesses (Promise) pour avoir un code clair et bien structuré.
Ensuite avec Promise.all c'est facile de faire une promesse qui se déclenche (attend, si on veut) quand les 3 ou 4 qui doivent être synchronisées sont toutes terminées.
Ca va encore plus loin avec l'API fetch qui simplifie beaucoup AJAX pour la récupération de données dans ce genre, et qui retourne directement une promesse sans devoir bidouiller, mais ce n'est pas encore supporté partout à moins d'utiliser du code de compatibilité.
QuentinC a écrit :
Bonne année, et vive mon 6000ème post !

Bravo pour cette importante somme de travail au service de tous!
QuentinC a écrit :
La remarque d'Olivier revient à ce dont j'avais déjà fait allusion dans un de mes posts précédents.
Pourquoi s'évertuer à utiliser AJAX quand tu pourrais charger directement la page avec son contenu final, avec PHP ou un autre langage côté serveur ?

Je me "m'évertue pas" à utiliser AJAX: je l'utilise simplement quand il correspond à mes besoins, comme n'importe quelle autre techno.
Je crois avoir expliqué pourquoi: si je voulais faire cela en PHP (ce que j'ai fait dans d'autre sites) il faudrait que le propriétaire du site -- qui, je le répète, est un égyptologue eta démarré son site en HTML basique -- se mette à écrire des choses dans un fichier qu'il ne connait pas, à savoir un fichier dont l'extension est '.php', qui contient des choses telles que <?php ... >?, include_once, des $ devant toutes les variables, etc.

QuentinC a écrit :
Tu as deux approches pour gérer l'asynchronisme:
1. Soit tu affiches quelque chose dès que c'est chargé
2. Soit tu attends que tout soit chargé avant d'afficher

Comment fais tu pour retarder l'affichage de l'ensemble de la page?
C'est bien le point qui m'arrête et pour lequel je demande de l'aide.
Pour l'instant, mon programme avec chargement asynchrone produit l'effet suivant:
1) il affiche la page avec les <section> vides
2) il remplit les <sections> avec ce qui vient de mes requêtes AJAX.
C'est tout à fait normal, mais l'effet sur l'utilisateur est désastreux, car le chargement prend environ 1 ou 2 dixièmes de secondes, assez pour que l'utilisateur voie un horrible clignotement.

QuentinC a écrit :
Même si tu choisis l'approche n°2, comme je l'ai déjà expliqué plus haut, tu gagneras en temps de chargement par rapport à la solution synchrone. Donc si tu persistes dans AJAX pour ce cas, oublie vraiment la version synchrone, elle est mauvaise à tout point de vue (sauf pour sa simplicité de programmation).

Franchement je me moque que le téléchargement et donc l'affichage prenne 1 ou 2 dixièmes de secondes de plus. Ce n'est pas la simplicité de la programmation qui me gêne (en 50 ans de carrière, j'en ai fait d'autres!). C'est quand j'ai eu terminé la version synchrone que je me suis rendu compte du résultat, ce que j'aurais dû anticiper.

QuentinC a écrit :
L'approche n°2 est peut-être un peu plus compliquée à mettre en place, mais pas tant que ça. Sur les navigateurs récents, tu peux utiliser les promesses (Promise) pour avoir un code clair et bien structuré.
Ensuite avec Promise.all c'est facile de faire une promesse qui se déclenche (attend, si on veut) quand les 3 ou 4 qui doivent être synchronisées sont toutes terminées.
Ca va encore plus loin avec l'API fetch qui simplifie beaucoup AJAX pour la récupération de données dans ce genre, et qui retourne directement une promesse sans devoir bidouiller, mais ce n'est pas encore supporté partout à moins d'utiliser du code de compatibilité.

Encore des choses qu'il faudrait que j'apprenne.
Mais avec mon âge et mon état de santé, je n'ai plus vraiment le temps d'apprendre de nouvelles choses si je n'en ai pas absolument besoin...
a écrit :
Comment fais tu pour retarder l'affichage de l'ensemble de la page?


JE ne pense pas que ce soit possible, et si c'était possible alors ce ne serait probablement pas judicieux. IL faut afficher des sections vides et les remplir dès que les données arrivent.

En ce qui concerne le clignotement, je ne peux pas répondre avec certitude, le CSS n'est pas mon domaine et je ne peux que difficilement m'en préoccuper. Mais je suppose que pour éviter l'effet "truc qui surgit de nulle part", il faut
1 - Commencer par placer le nouveau contenu en display:none/visibility:hidden/opacity:0/width:0px/height:0px/toute autre astuce du même genre; voir quelles instructions sont suceptibles de faire en sorte que le contenu soit prérendu hors écran à la mode double-buffering. Quelqu'un y a sûrement déjà pensé.
2 - jouer sur des effets d'animation qui font apparaître le contenu progressivement; pour des effets modernes, voir les modules transition et animation de CSS3.

JE persiste quand même dans l'idée que tu t'arracherais bien moins les cheveux avec du PHP.
Tu as sûrement les moyens de faire quelque chose d'assez simple que ton client peut comprendre, sans pour autant aller jusqu'à faire quelque chose d'aussi complet qu'un véritable CMS.
QuentinC a écrit :
Comment fais tu pour retarder l'affichage de l'ensemble de la page?

Je ne pense pas que ce soit possible, et si c'était possible alors ce ne serait probablement pas judicieux.

C'est tout à fait possible et judicieux: il suffit de travailler en synchrone au cours du chargement de la page. Ce chargement dure quelques dixièmes de seconde de plus, mais quand ça s'affiche tout est là.

J'ai passé une grande partie de la journée à essayer d'autres technos, en particulier l'un de mes essais consistait à importer non pas le HTML tout fait par un appel AJAX au serveur, mais à générer des balises <script type="text/javascript" src="xxx.php?suite de paramètres"></script>. C'était l'une des pistes que j'avais envisagées au départ, mais après un certain nombre de tests, cela introduit d'autres complexités et nécessite le redesign complet de l'appli.
Noter cependant que cette approche rend bien les chargements asynchrones, puisque chaque fois que le moteur JavaScript rencontre une telle balise il effectue le chargement du JavaScript en asynchrone.

Qu'on se comprenne bien: je ne prétends pas que le fonctionnement en synchrone soit à utiliser dans tous les cas, bien au contraire, mais dans ce cas précis, cela semble être la seule solution qui soit cohérente avec les objectifs recherchés.

Comme dit Raphaël
Raphael a écrit :
C'est une "bonne pratique" générale, mais cela dépend de ton usage et de tes besoins en effet.

Moyennant quoi ces heures n'ont pas été perdues, je penserai effectivement à utiliser l'asynchrone quand cela me paraitra approprié.

Le grand intérêt de cette discussion c'est de m'avoir fait comprendre pas mal de choses, et je vous remercie tous pour les échanges que nous avons eus sur ce sujet.
a écrit :
C'est tout à fait possible et judicieux: il suffit de travailler en synchrone au cours du chargement de la page. Ce chargement dure quelques dixièmes de seconde de plus, mais quand ça s'affiche tout est là.


C'est judicieux, mais à faire côté serveur, pas en JavaScript.

Côté client ça fait des requêtes pour rien et ça peut prendre bien plus que juste quelques dizièmes de seconde. En tout cas de mon côté je ne vois toujours pas l'intérêt de faire de l'AJAX si c'est pour rester synchrone.
QuentinC a écrit :

Côté client ça fait des requêtes pour rien et ça peut prendre bien plus que juste quelques dixièmes de seconde.

Quand on fait un <link> vers une page CSS ou un <script src="...">, ou même simplement une balise <img src="..."> ça fait aussi des échanges sur le réseau.
Il est vrai que ces échanges sont asynchrones l'un par rapport à l'autre, mais il faut attendre que tout soit terminé pour que l'affichage se produise.
Ce que j'ai besoin de faire, c'est la même chose que l'affichage d'une image, sauf que ce n'est pas une image mais du HTML. Je ne connais pas d'autres moyens de le faire que de mettre un <iframe> ou de travailler en AJAX. Mais dans les deux cas c'est asynchrone et ça se voit, car la page s'affiche avant que le chargement soit effectif. Le fait qu'il existe une possibilité de faire de l'AJAX synchrone est donc la bonne solution à mon problème, du moins me semble-t-il.
QuentinC a écrit :
En tout cas de mon côté je ne vois toujours pas l'intérêt de faire de l'AJAX si c'est pour rester synchrone.

L'intérêt de l'AJAX, c'est avant tout de pouvoir intégrer des données depuis un serveur dans une page HTML. Que ce soit synchrone ou asynchrone me semble un point secondaire.

SI je reviens à mon cas de figure, il y a d'une part une page HTML écrite par le propriétaire du site, d'autre part le serveur qui contient le programme PHP dont je suis l'auteur.
Cette technique nous permet de conserver une relative indépendance, entre l'égyptologue qui n'a besoin de connaître que quelques bases du HTML, sans avoir à connaître la programmation, et moi qui ait quelques connaissances techniques (dont 50 ans d'expérience de la programmation) mais qui n'ai pas à décider de ce qui doit se trouver dans une page donnée.
C'est certainement un cas particulier, mais c'est celui que j'ai à traiter.
Salut,

Je reviens peu être un peu en retard sur le sujet mais je tenais quand même à t'apporter mon point de vue. Premièrement, je pense que AJAX devrait bel et bien s'utiliser de manière asynchrone. Y'a qu'à regarder la signification des acronymes et ça en dit long (Asynchronous JAvascript and XML).

Ensuite, on dirait que ton client à simplement besoin d'une gestion de contenu très simplifiée. Je te conseillerais la même chose que QuentinC, à savoir générer cela en PHP. Au final, tu obtiendras un code de meilleure qualité. J'ai pas encore 50 ans d'expérience en programmation, mais je suis presque sûr en avançant qu'il vaut mieux ne pas utiliser de fonctions obsolètes/dépréciées.

Bye!

Edit : En fait, n'importe quelle techno, tant que c'est pas de l'AJAX en synchrone
Modifié par Anymah (10 Jan 2017 - 13:55)