2 votes

condition de course possible pour un appel ajax en javascript

J'implémente donc en javascript cette fonctionnalité : lorsque le document est ouvert par l'utilisateur, ses données sont sauvegardées toutes les 10 secondes et lorsque l'utilisateur le ferme, il est sauvegardé une fois de plus. Cela fonctionne apparemment bien. Voici l'implémentation :

var data = "blabla";
var saveagain = false;
var t1;

function onDocumentOpen() {
    saveagain = true;
    savedata();
}

function onDocumentClose() {
    saveagain = false;
    savedata();
}

function savedata() {
    if (!saveagain) clearTimeout(t1);
    SaveDataAjaxCall(data, function() {
        //data was saved
        if (saveagain) t1 = setTimeout("savedata()", 10000);
    });
}

Je me demandais si mon approche était correcte et si elle pouvait conduire à une situation de course dans des circonstances extrêmes, par exemple :

lorsque l'instance savedata() appelée par onDocumentClose() se trouve après l'étape if(!saveagain), l'instance savedata() appelée par le timer précédent de setTimeout() se trouve avant cette étape, elle est donc appelée une fois de plus. Est-ce que cela ou quelque chose de plus bizarre peut arriver ?

Merci d'avance

EDITAR:

Après avoir pris en compte les commentaires de T.J. Crowder et de Bengi, j'ai finalisé le code comme tel :

var data = "";
var saveagain = false;
var t1;
var beingsaved = false;

function onDocumentOpen() {
    saveagain = true;
    savedata();
}

function onDocumentClose() {
    saveagain = false;
    savedata();
}

function saveData() {
    if (beingsaved) {
        if (!saveagain) setTimeout(saveData, 100);
        return false;
    }
    beingsaved=true;

    if (!saveagain) clearTimeout(t1);

    data=getData();

    SaveDataAjaxCall(data, function() {
        //data was saved
        beingsaved=false;
        if (saveagain) t1 = setTimeout(saveData, 10000);
    });

}

Je pense que j'ai géré toutes les occasions maintenant. Je pense que la solution "beingsaved" est égale au compte atomique que T.J Crowder a suggéré.

EDIT2 : Hm, je ne suis pas sûr d'avoir résolu le problème car il peut y avoir un cas où if(beingsaved) est évalué par l'appel setTimeout JUSTE avant que beingsaved soit mis à true par l'appel onDocumentClose. Cela peut-il arriver ?

2voto

T.J. Crowder Points 285826

Je suppose que votre opération de sauvegarde est asynchrone, comme toute bonne opération ajax doit l'être. Si c'est le cas, vous devrez mettre une sorte de condition de garde autour d'elle afin que les deux déclencheurs de sauvegarde ne se chevauchent pas. Ou, bien sûr, autorisez-les à se chevaucher mais gérez-les côté serveur (peut-être avec un numéro de séquence ou un horodatage), en ignorant une sauvegarde antérieure si une sauvegarde ultérieure a déjà été effectuée.

1voto

Bergi Points 104242

Non. Javascript est monofilaire (à l'exception d'innovations comme WebWorkers et Cie, qui doivent communiquer sur une interface basée sur les événements).

Ainsi, une fois que votre exécution synchrone est lancée (le script global, le timeout, le gestionnaire d'événement ajax), elle s'exécute et ne peut pas être arrêtée. Tout le reste (nouveaux événements, 0ms-timeouts, etc.) sera programmé après.

Votre script contient 2 scénarios asynchrones : le timeout et le callback ajax. Après avoir lancé la boucle onDocumentOpen, ça se passe comme ça :

  1. exécuter saveData : lancer une requête ajax
  2. attendre (jusqu'à ce que l'événement ajax se produise)
  3. exécuter le callback de succès : définir un nouveau timeout pour saveData
  4. attendre (jusqu'à ce que l'événement de timeout se produise)
  5. exécuter saveData : lancer une requête ajax
  6. Attendez...

...et ainsi de suite.

Votre onDocumentClose ne peut s'exécuter que pendant une période d'attente, lorsqu'aucune autre exécution ne se déroule. On peut s'attendre à ce qui suit :

  1. ...exécuter saveData : lancer une requête ajax
  2. rien ne se passe
  3. événement ajax : exécuter le callback de succès : définir un nouveau délai d'attente pour saveData
  4. rien ne se passe
  5. événement documentClose : efface le délai d'attente, lance la requête ajax
  6. rien ne se passe
  7. événement ajax : exécution du callback de succès : ne fixe plus de nouveau délai d'attente. Fin.

Mais vous n'avez pas sécurisé le cas où documentClose se produit pendant la requête ajax :

  1. ...exécuter saveData : lancer une requête ajax
  2. rien ne se passe
  3. événement ajax : exécuter le callback de succès : définir un nouveau délai d'attente pour saveData
  4. rien ne se passe
  5. événement de timeout : exécution de saveData, lancement de la requête ajax
  6. rien ne se passe
  7. événement documentClose : efface le délai d'attente (inexistant), lance la requête ajax (une deuxième fois)
  8. rien ne se passe
  9. l'un des événements ajax : exécuter le callback de succès : ne fixe plus un nouveau délai d'attente.
  10. rien ne se passe
  11. autre événement ajax : exécution du callback de succès : ne fixe plus de nouveau délai. Fin.

Il y aura donc toujours une fin. Si l'un des événements se déclenchait pendant l'exécution de quelque chose, il n'y aurait pas de "rien ne se passe" entre les deux, mais les événements seraient exécutés les uns après les autres. Même si le délai d'attente devait se terminer pendant l'exécution de la callback ajax et que avant qu'elle ne soit effacée il sera déprogrammé quand il sera nettoyé après sa fin :

var id = setTimeout(function(){
    alert("still waiting for execution"); // never alerts
}, 500);
setTimeout(function(){
    alert("still waiting for execution"); // this alerts
}, 500);
for(var d = Date.now(); Date.now()-d < 1000; ) {
    ; // wait - the timeouts end during this heavy processing
}
clearTimeout(id);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X