XHR et portée de variables

Mammouth du PHP | 19672 Messages

05 mars 2006, 11:30

Bonjour,
je reviens à la charge avec XHR et mon horloge, il y a un fonctionnement qui m'échappe.

J'ai un problème de portée de variable et il semble qu'avec un objet XHR, les règles normales à propos des variables globales/locales ne fonctionnent pas de la même manière : est-ce que quelqu'un aurait une explication où un lien vers une doc appropriée que je n'aurais pas trouvée.

Explications.
Mon but est basique : je crée une horloge dans un coin de page, horloge que je veux synchroniser périodiquement avec l'heure du serveur. Souhaitant le faire sans recharger toute la page, j'utilise un objet XHR. Jusque là, rien de compliqué. Ma fonction tourne de façon récursive avec un setTimeout toutes les secondes de façon à afficher l'heure au format HH:mn:sc. Je n'ai pas besoin de la date du jour.

Voici la fonction complète :
/**
* Fonction de rotation de l'horloge
* La fonction reçoit en paramètre l'heure courante  sous forme de nombre de secondes ((HH * 3600) + (mn * 60) + sc)
*/
function horloge(nbSecondes)
{
    var ts;
    var msgDebug = '';
    var temporisation = 1000;
    /* Recalage toutes les cinq secondes */
    if((ts % 5) == 0)
    {
        getXhr();
        /* On définit ce qu'on va faire quand on aura la réponse */
        xhr.onreadystatechange = function()
        {
            /* On ne fait quelque chose que si on a tout reçu et que le serveur est ok */
            if(xhr.readyState == 4)
            {
                if(xhr.status == 200)
                {
                    ts = parseInt(xhr.responseText);
                    document.getElementById('AfficheHeure').innerHTML = 'Origine : '+ nbSecondes +'<br />\nRecalage : '+ ts;
                    msgDebug = 'Heure rechargée par XHR :<br \/>\n'+ ts;
                }
                else
                {
                    msgDebug = 'Statut retour '+ xhr.status;
                }
            }
            else
            {
            }
        }
        xhr.open("GET", xhr_secondes);
        xhr.send(null);
        document.getElementById('controle').innerHTML = 'Point de contrôle <em>ts<\/em> :<br \/>\n'+ ts;
        document.getElementById('msg_delai').innerHTML = 'Nouveau point de référence (1) <em>ts<\/em> :<br \/>\n'+ ts;
    }
    else
    {
        ts = (nbSecondes == 86400) ? 0 : nbSecondes
        msgDebug = 'On utilise l\'heure de la boucle normale :<br \/>\n'+ ts;
        document.getElementById('msg_delai').innerHTML = 'Nouveau point de référence (2) <em>ts<\/em> :<br \/>\n'+ ts;
    }
    afficherHeure(parseInt(ts));
    ts++;
    document.getElementById('msg_debug').innerHTML = msgDebug;
    setTimeout('horloge("'+ ts +'")', temporisation);
}
J'ai ajouté un paquet de messages pour afficher un peu ce qui se passe. Voici la nature du problème. Ma variable ts est théoriquement globale à l'intérieur de cette fonction. Ça reste purement théorique puisque dans la pratique, toutes les 5 secondes (délai défini uniquement pour les besoins de test) elle devrait être ajustée via l'appel XHR et redéfinie à la ligne 22 de ce bout de code. Le problème, c'est précisément que cette valeur ne semble pas sortir des accolades du if() entre les lignes 21 et 25 : et je ne comprends pas pourquoi.

D'autre part, je ne saisis pas le fonctionnement de onreadystatechange : l'affichage obtenu me met simultanément les messages des lignes 23 et 28, cette dernière affichant toujours la valeur 1 mais jamais 4, et je ne vois jamais le message de la ligne 24.

Et enfin, comment recaler la valeur de ma variable ts globalement dans ma fonction à partir d'un XHR ?
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Mammouth du PHP | 19672 Messages

05 mars 2006, 19:19

Bon, j'ai enfin réussi à obtenir ce que je voulais en contournant le problème. Cependant, comme ça ne répond pas à ma question, je laisse le sujet ouvert en espérant qu'un Gourou du JavaScript saura m'expliquer ce petit mystère.

Éventuellement pour ceux qui souhaiteraient savoir comment j'ai résolu mon problème, voici la fonction corrigée :
/**
* Fonction de rotation de l'horloge
* La fonction reçoit en paramètre l'heure courante  sous forme de nombre de secondes
*/
function horloge(nbSecondes)
{
    var bench1 = new Date();
	var benchDebut = bench1.getTime();
	var ts = (nbSecondes == 86400) ? 0 : nbSecondes
    var msgDebug = '';
    var temporisation = 1000;
    /* Recalage toutes les cinq secondes */
    if((ts % 5) == 0)
    {
        getXhr();
        /* On définit ce qu'on va faire quand on aura la réponse */
        xhr.onreadystatechange = function()
        {
            /* On ne fait quelque chose que si on a tout reçu et que le serveur est ok */
            if(xhr.readyState == 4)
            {
                if(xhr.status == 200)
                {
                    ts = parseInt(xhr.responseText);
                    document.getElementById('AfficheHeure').innerHTML = 'Origine : '+ nbSecondes +'<br />\nRecalage : '+ ts;
                    msgDebug = 'Heure rechargée par XHR :<br \/>\n'+ ts;
                    afficherHeure(parseInt(ts));
                    ts++;
                    document.getElementById('msg_debug').innerHTML = msgDebug;
                    var bench2 = new Date();
                	var benchFin = bench2.getTime();
                	var temporisationReelle = temporisation - (benchFin - benchDebut);
                    document.getElementById('controle').innerHTML = 'Temporisation réelle : '+ benchFin +' - '+ benchFin +' = '+ temporisationReelle;
                    setTimeout('horloge("'+ ts +'")', temporisationReelle);
                }
                else
                {
                    msgDebug = 'Statut retour '+ xhr.status;
                }
            }
            else
            {
			    msgDebug = 'Heure non rechargée par XHR :<br \/>\n'+ ts;
            }
        }
        xhr.open("GET", xhr_secondes);
        xhr.send(null);
        document.getElementById('controle').innerHTML = 'Point de contrôle <em>ts<\/em> :<br \/>\n'+ ts;
        document.getElementById('msg_delai').innerHTML = 'Nouveau point de référence (1) <em>ts<\/em> :<br \/>\n'+ ts;
    }
    else
    {
        msgDebug = 'On utilise l\'heure de la boucle normale :<br \/>\n'+ ts;
        document.getElementById('msg_delai').innerHTML = 'Nouveau point de référence (2) <em>ts<\/em> :<br \/>\n'+ ts;
        afficherHeure(parseInt(ts));
        ts++;
        document.getElementById('msg_debug').innerHTML = msgDebug;
        var bench2 = new Date();
    	var benchFin = bench2.getTime();
    	var temporisationReelle = temporisation - (benchFin - benchDebut);
        document.getElementById('controle').innerHTML = 'Temporisation réelle : '+ benchFin +' - '+ benchFin +' = '+ temporisationReelle;
        setTimeout('horloge("'+ ts +'")', temporisationReelle);
    }
}
En fait, si on reprend la fonction d'origine, j'ai pris tout ce qui se trouvait entre la dernière accolade de la fonction et la dernière accolade du premier else et je l'ai collé d'une part à l'intérieur de ce même else et une seconde fois dans le if(xhr.status == 200).

Et là mon recalage fonctionne.

J'ai également ajouté des prises instantanées pour modifier éventuellement la temporisation du timeout selon la durée d'exécution du code de façon à rattraper également ce décallage et ça fonctionne également.

Mais encore une fois, ça ne résoud pas ma question originale : quelle est donc la portée des variables modifiées à l'intérieur du if(xhr.status == 200){} ?
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Administrateur PHPfrance
Administrateur PHPfrance | 3131 Messages

05 mars 2006, 20:18

Mmmh bizarre je vais me pencher dessus. J'avais pourtant testé un script censé montrer la portée des variables dans un objet xhr :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Portйe XHR</title>
<script type="text/javascript">
// <![CDATA[ 

var d = null;
function log(str) { 
    if (!d)
        d = document.getElementById("log");
    if (d)
        d.innerHTML += "\r\n"+str;
}

function returnXHR() {
    var conn;
    try { conn = new XMLHttpRequest(); }
    catch (error) {
        if (debug) { alert('Erreur lors de la tentative de crйation de l\'objet \nnew XMLHttpRequest()\n\n' + error); }
        try { conn = new ActiveXObject("Microsoft.XMLHTTP"); }
        catch (error) {
            if (debug) { alert('Erreur lors de la tentative de crйation de l\'objet \nnew ActiveXObject("Microsoft.XMLHTTP")\n\n' + error); }
            try { conn = new ActiveXObject("Msxml2.XMLHTTP"); }
            catch (error) {
                if (debug) { alert('Erreur lors de la tentative de crйation de l\'objet \nnew ActiveXObject("Msxml2.XMLHTTP")\n\n' + error); }
                conn = false;
            }
        }
    }
    return conn;
}

var xhrglobal2;
function globalXHR() {
    try { xhrglobal2 = new XMLHttpRequest(); }
    catch (error) {
        if (debug) { alert('Erreur lors de la tentative de crйation de l\'objet \nnew XMLHttpRequest()\n\n' + error); }
        try { xhrglobal2 = new ActiveXObject("Microsoft.XMLHTTP"); }
        catch (error) {
            if (debug) { alert('Erreur lors de la tentative de crйation de l\'objet \nnew ActiveXObject("Microsoft.XMLHTTP")\n\n' + error); }
            try { xhrglobal2 = new ActiveXObject("Msxml2.XMLHTTP"); }
            catch (error) {
                if (debug) { alert('Erreur lors de la tentative de crйation de l\'objet \nnew ActiveXObject("Msxml2.XMLHTTP")\n\n' + error); }
                xhrglobal2 = false;
            }
        }
    }
}


var globale = 1;
var xhrglobal1 = returnXHR();
function fonction(argument) 
{ 
    
    var locale = 1;
    try { log("fonction.argument(read) = "+argument); } catch (e) { log("fonction.argument(read) = ERROR : "+e); }
    try { argument = 2; log("fonction.argument(write:2) = "+argument); } catch (e) { log("fonction.argument(write:2) = ERROR : "+e); }
    try { log("fonction.locale(read) = "+locale); } catch (e) { log("fonction.locale(read) = ERROR : "+e); }
    try { locale = 2; log("fonction.locale(write:2) = "+locale); } catch (e) { log("fonction.locale(write:2) = ERROR : "+e); }
    try { log("fonction.globale(read) = "+globale); } catch (e) { log("fonction.globale(read) = ERROR : "+e); }
    try { globale = 2; log("fonction.globale(write:2) = "+globale); } catch (e) { log("fonction.globale(write:2) = ERROR : "+e); }
    log("");
    
    var xhrlocal = returnXHR();
    xhrlocal.onreadystatechange = function () {
        if (xhrlocal.readyState == 4) {
            if (xhrlocal.status == 200) {
                try { log("xhrlocal.argument(read) = "+argument); } catch (e) { log("xhrlocal.argument(read) = ERROR : "+e); }
                try { argument = 3; log("xhrlocal.argument(write:3) = "+argument); } catch (e) { log("xhrlocal.argument(write:3) = ERROR : "+e); }
                try { log("xhrlocal.locale(read) = "+locale); } catch (e) { log("xhrlocal.locale(read) = ERROR : "+e); }
                try { locale = 3; log("xhrlocal.locale(write:3) = "+locale); } catch (e) { log("xhrlocal.locale(write:3) = ERROR : "+e); }
                try { log("xhrlocal.globale(read) = "+globale); } catch (e) { log("xhrlocal.globale(read) = ERROR : "+e); }
                try { globale = 3; log("xhrlocal.globale(write:3) = "+globale); } catch (e) { log("xhrlocal.globale(write:3) = ERROR : "+e); }
                log("");
            }
        }
    }
    xhrlocal.open("GET", "test.txt");
    xhrlocal.send("");

    xhrglobal1.onreadystatechange = function () {
        if (xhrglobal1.readyState == 4) {
            if (xhrglobal1.status == 200) {
                try { log("xhrglobal1.argument(read) = "+argument); } catch (e) { log("xhrglobal1.argument(read) = ERROR : "+e); }
                try { argument = 4; log("xhrglobal1.argument(write:4) = "+argument); } catch (e) { log("xhrglobal1.argument(write:4) = ERROR : "+e); }
                try { log("xhrglobal1.locale(read) = "+locale); } catch (e) { log("xhrglobal1.locale(read) = ERROR : "+e); }
                try { locale = 4; log("xhrglobal1.locale(write:4) = "+locale); } catch (e) { log("xhrglobal1.locale(write:4) = ERROR : "+e); }
                try { log("xhrglobal1.globale(read) = "+globale); } catch (e) { log("xhrglobal1.globale(read) = ERROR : "+e); }
                try { globale = 4; log("xhrglobal1.globale(write:4) = "+globale); } catch (e) { log("xhrglobal1.globale(write:4) = ERROR : "+e); }
                log("");
            }
        }
    }
    xhrglobal1.open("GET", "test.txt");
    xhrglobal1.send("");

    globalXHR();
    xhrglobal2.onreadystatechange = function () {
        if (xhrglobal2.readyState == 4) {
            if (xhrglobal2.status == 200) {
                try { log("xhrglobal2.argument(read) = "+argument); } catch (e) { log("xhrglobal2.argument(read) = ERROR : "+e); }
                try { argument = 5; log("xhrglobal2.argument(write:5) = "+argument); } catch (e) { log("xhrglobal2.argument(write:5) = ERROR : "+e); }
                try { log("xhrglobal2.locale(read) = "+locale); } catch (e) { log("xhrglobal2.locale(read) = ERROR : "+e); }
                try { locale = 5; log("xhrglobal2.locale(write:5) = "+locale); } catch (e) { log("xhrglobal2.locale(write:5) = ERROR : "+e); }
                try { log("xhrglobal2.globale(read) = "+globale); } catch (e) { log("xhrglobal2.globale(read) = ERROR : "+e); }
                try { globale = 5; log("xhrglobal2.globale(write:5) = "+globale); } catch (e) { log("xhrglobal2.globale(write:5) = ERROR : "+e); }
                log("");
            }
        }
    }
    xhrglobal2.open("GET", "test.txt");
    xhrglobal2.send("");

}
// ]]>
</script>
</head>
<body>
<pre id="log"></pre>
<script type="text/javascript">/* <![CDATA[ */ fonction(1); /* ]]> */</script>
</body>
</html>
fonction.argument(read) = 1
fonction.argument(write:2) = 2
fonction.locale(read) = 1
fonction.locale(write:2) = 2
fonction.globale(read) = 1
fonction.globale(write:2) = 2

xhrlocal.argument(read) = 2
xhrlocal.argument(write:3) = 3
xhrlocal.locale(read) = 2
xhrlocal.locale(write:3) = 3
xhrlocal.globale(read) = 2
xhrlocal.globale(write:3) = 3

xhrglobal1.argument(read) = 3
xhrglobal1.argument(write:4) = 4
xhrglobal1.locale(read) = 3
xhrglobal1.locale(write:4) = 4
xhrglobal1.globale(read) = 3
xhrglobal1.globale(write:4) = 4

xhrglobal2.argument(read) = 4
xhrglobal2.argument(write:5) = 5
xhrglobal2.locale(read) = 4
xhrglobal2.locale(write:5) = 5
xhrglobal2.globale(read) = 4
xhrglobal2.globale(write:5) = 5
Ce qui démontrait que toutes les variables étaient également accessibles/inscriptibles.

Je regarde ton script et je te dis si je trouve quelque chose. Ta fonction getXHR() est la même que ma fonction globalXHR() ?
Tu pourrais m'envoyer la page complète (le script PHP + la page HTML + les librairies JS) le problème vient peut-être d'un élément extérieur...

Mammouth du PHP | 19672 Messages

05 mars 2006, 20:49

Si tu y tiens, je t'envoie ça par MP.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe: