74 votes

Comment forcer l'exécution séquentielle de Javascript ?

J'ai cherché un peu partout la réponse à ce problème apparemment simple, mais je n'ai trouvé que des réponses plutôt compliquées impliquant des classes, des gestionnaires d'événements et des callbacks (qui me semblent être une approche un peu brutale). Je pense que les callbacks peuvent être utiles mais je n'arrive pas à les appliquer dans le contexte le plus simple. Voir cet exemple :

<html>
  <head>
    <script type="text/javascript">
      function myfunction()  {
        longfunctionfirst();
        shortfunctionsecond();
      }

      function longfunctionfirst() {
        setTimeout('alert("first function finished");',3000);
      }

      function shortfunctionsecond() {
        setTimeout('alert("second function finished");',200);
      }
    </script>
  </head>
  <body>
    <a href="#" onclick="javascript:myfunction();return false;">Call my function</a>
  </body>
</html>

Dans ce cas, la deuxième fonction s'achève avant la première ; quelle est la manière la plus simple (ou en existe-t-il une ?) de forcer la deuxième fonction à retarder son exécution jusqu'à ce que la première fonction soit terminée ?

---Edit---

Merci pour toutes les réponses si rapides !

C'était donc un mauvais exemple, mais grâce à David Hedlund, je vois avec ce nouvel exemple que c'est effectivement synchrone (et que mon navigateur s'est planté pendant le processus de test !)

<html>
<head>

<script type="text/javascript">
function myfunction() {
    longfunctionfirst();
    shortfunctionsecond();
}

function longfunctionfirst() {
    var j = 10000;
    for (var i=0; i<j; i++) {
        document.body.innerHTML += i;
    }
    alert("first function finished");
}

function shortfunctionsecond() {
    var j = 10;
    for (var i=0; i<j; i++) {
        document.body.innerHTML += i;
    }
    alert("second function finished");
}
</script>

</head>

<body>
  <a href="#" onclick="javascript:myfunction();return false;">Call my function</a>
</body>
</html>

Comme mon problème RÉEL était avec jquery et IE, je vais devoir poster une question séparée à ce sujet si je ne peux pas obtenir quelque chose moi-même !

0 votes

J'espère que cela vous sera utile github.com/dineshkani24/queuecall

0 votes

Je suis content que ce ne soit pas moi qui doive lutter contre ça. J'ai commencé à utiliser nodejs pour le traitement des fichiers de logs. Tout dans ce traitement impose un fonctionnement séquentiel, mais j'ai passé tellement de temps à essayer de forcer les séquences requises. Je trouve qu'avec les promesses et tout l'enchaînement de "puis ceci" "puis cela", on se retrouve avec un code illisible.

51voto

David Hedlund Points 66192

Bien, setTimeout par sa définition, ne maintiendra pas le fil. C'est souhaitable, car si c'était le cas, l'interface utilisateur entière serait gelée pendant le temps d'attente. Si vous avez vraiment besoin d'utiliser setTimeout alors vous devez utiliser des fonctions de rappel :

function myfunction() {
    longfunctionfirst(shortfunctionsecond);
}

function longfunctionfirst(callback) {
    setTimeout(function() {
        alert('first function finished');
        if(typeof callback == 'function')
            callback();
    }, 3000);
};

function shortfunctionsecond() {
    setTimeout('alert("second function finished");', 200);
};

Si vous êtes pas en utilisant setTimeout mais nous n'avons que des fonctions qui s'exécutent très longtemps, et nous avons utilisé la fonction setTimeout pour simuler cela, alors vos fonctions serait sont en fait synchrones, et vous n'auriez pas du tout ce problème. Il convient toutefois de noter que les requêtes AJAX sont asynchrones et que, tout comme les requêtes AJAX, les requêtes AJAX ne sont pas synchrones. setTimeout ne retient pas le fil de l'interface utilisateur jusqu'à ce qu'il soit terminé. Avec AJAX, comme avec setTimeout vous devrez travailler avec des rappels.

4 votes

De plus, je dois noter que les requêtes AJAX sont généralement asynchrone, mais peut être rendu synchrone.

0 votes

@Justin : oui, c'est un point valable, merci pour la remarque. Si le scénario réel du problème est effectivement lié à ajax, les callbacks synchrones pourraient être la solution.

0 votes

(bien que l'approche async avec callback offre probablement un meilleur contrôle et une plus grande lisibilité du flux d'exécution).

38voto

Raymond Points 166

Je reviens à cette question après tout ce temps car il m'a fallu tout ce temps pour trouver ce que je pense être une solution propre : La seule façon de forcer une exécution séquentielle de javascript que je connaisse est d'utiliser les promesses. Il y a des explications exhaustives sur les promesses à : Promesses/A et Promesses/A+

La seule bibliothèque qui implémente les promesses que je connais est jquery. Voici donc comment je résoudrais la question en utilisant jquery promises :

<html>
<head>
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script type="text/javascript">
    function myfunction()
    {
        promise = longfunctionfirst().then(shortfunctionsecond);
    }
    function longfunctionfirst()
    {
        d = new $.Deferred();
        setTimeout('alert("first function finished");d.resolve()',3000);
        return d.promise()
    }
    function shortfunctionsecond()
    {
        d = new $.Deferred();
        setTimeout('alert("second function finished");d.resolve()',200);
        return d.promise()
    }
    </script>
</head>
<body>
    <a href="#" onclick="javascript:myfunction();return false;">Call my function</a>
</body>
</html>

En implémentant une promesse et en enchaînant les fonctions avec .then(), vous vous assurez que la seconde fonction ne sera exécutée qu'après l'exécution de la première. C'est la commande d.resolve() dans longfunctionfirst() qui donne le signal de démarrage de la fonction suivante.

Techniquement, la shortfunctionsecond() n'a pas besoin de créer un différé et de retourner une promesse, mais je suis tombé amoureux des promesses et j'ai tendance à tout implémenter avec des promesses, désolé.

1 votes

Les promesses font en fait partie du projet ECMAscript actuel (2015) : people.mozilla.org/~jorendorff/

0 votes

Les gens lisent encore cette vieille réponse ? Merci, oui Javascript évolue, j'attends juste que la version 2015 rattrape tous les navigateurs existants :-)

2 votes

Bonjour à tous, je suis toujours surpris de voir que ces vieux sujets attirent toujours l'attention. Oui, vous avez raison, maintenant ES6 et un nombre croissant de frameworks implémentent les promesses. C'est une vieille histoire, mais elle m'a mis au pied du mur il y a 5 ans et a été la leçon la plus difficile que j'ai apprise en informatique. Pour faire simple, j'étais coincé dans une compréhension séquentielle de l'informatique à la Von Newmann et je ne pouvais pas saisir la beauté du développement événementiel embrassé par Javascript.

13voto

Raymond Points 166

Je suis un vieux routier de la programmation et je suis revenu récemment à mon ancienne passion. J'ai du mal à m'adapter à ce nouveau monde brillant, orienté objet et événementiel, et bien que je voie les avantages du comportement non séquentiel de Javascript, il y a des moments où cela nuit vraiment à la simplicité et à la réutilisation. Un exemple simple sur lequel j'ai travaillé était de prendre une photo (téléphone portable programmé en javascript, HTML, phonegap, ...), de la redimensionner et de la télécharger sur un site web. La séquence idéale est :

  1. Prenez une photo
  2. Charger la photo dans un élément img
  3. Redimensionner l'image (en utilisant Pixastic)
  4. Téléchargez-le sur un site web
  5. Informer l'utilisateur en cas de succès ou d'échec

Tout ceci constituerait un programme séquentiel très simple si chaque étape renvoyait le contrôle à la suivante lorsqu'elle était terminée, mais en réalité.. :

  1. Prendre une photo est asynchrone, le programme tente donc de la charger dans l'élément img avant qu'elle n'existe.
  2. Le chargement de la photo est asynchrone, de sorte que le redimensionnement de l'image commence avant le chargement complet de l'image.
  3. Le redimensionnement est asynchrone, de sorte que le téléchargement vers le site Web commence avant que l'image ne soit complètement redimensionnée.
  4. Le téléchargement vers le site web est asynchrone, le programme continue donc avant que la photo soit complètement téléchargée.

Et en fait, 4 des 5 étapes impliquent des fonctions de rappel.

Ma solution est donc d'imbriquer chaque étape dans la précédente et d'utiliser .onload et d'autres stratagèmes similaires, Cela ressemble à quelque chose comme ceci :

takeAPhoto(takeaphotocallback(photo) {
  photo.onload = function () {
    resizePhoto(photo, resizePhotoCallback(photo) {
      uploadPhoto(photo, uploadPhotoCallback(status) {
        informUserOnOutcome();
      });
    }); 
  };
  loadPhoto(photo);
});

(J'espère que je n'ai pas fait trop d'erreurs en ramenant le code à l'essentiel, la réalité est trop distrayante).

Je crois que c'est un exemple parfait où l'asynchronisme n'est pas bon et la synchronisation est bonne, parce que contrairement à la gestion des événements de l'interface utilisateur, chaque étape doit être terminée avant que la suivante ne soit exécutée, mais le code est une construction de poupées russes, il est confus et illisible, la réutilisabilité du code est difficile à réaliser à cause de toutes les imbrications, il est tout simplement difficile d'apporter à la fonction interne tous les paramètres nécessaires sans les passer à chaque conteneur à tour de rôle ou utiliser des variables globales maléfiques, et j'aurais aimé que le résultat de tout ce code me donne un code de retour, mais le premier conteneur sera terminé bien avant que le code de retour soit disponible.

Pour revenir à la question initiale de Tom, quelle serait la solution intelligente, facile à lire et à réutiliser pour ce qui aurait été un programme très simple il y a 15 ans, en utilisant disons le langage C et une carte électronique débile ?

L'exigence est en fait si simple que j'ai l'impression qu'il doit me manquer une compréhension fondamentale de Javsascript et de la programmation moderne, la technologie étant censée alimenter la productivité, n'est-ce pas ?

Merci de votre patience

Raymond le dinosaure ;-)

3 votes

J'ai rencontré les mêmes difficultés avec phonegap. J'ai un code rempli de fonctions de rappel imbriquées, ce qui est difficile à lire. À un moment donné, j'ai regretté de ne pas avoir implémenté l'application en mode natif. Je travaillais sur un enregistreur audio et la synchronisation des données enregistrées avec le serveur. J'ai essayé de simplifier mon code avec requirejs et backbone. Cela a aidé un peu à démonter les choses et à organiser le code en modules, mais j'ai toujours besoin d'imbriquer les callbacks...

2voto

Franz Points 539

Dans votre exemple, la première fonction se termine effectivement avant que la seconde ne soit lancée. setTimeout ne maintient pas l'exécution de la fonction jusqu'à ce que le délai soit atteint, il démarre simplement une minuterie en arrière-plan et exécute votre instruction d'alerte après le délai spécifié.

Il n'y a pas de moyen natif de faire un "sleep" en JavaScript. Vous pouvez écrire une boucle qui vérifie l'heure, mais cela demandera beaucoup d'efforts au client. Vous pourriez aussi faire l'appel AJAX synchrone, comme emacsian l'a décrit, mais cela entraînera une charge supplémentaire sur votre serveur. Votre meilleure chance est vraiment d'éviter cela, ce qui devrait être assez simple pour la plupart des cas une fois que vous aurez compris comment fonctionne setTimeout.

1voto

rampr Points 642

En javascript, il n'y a aucun moyen de faire attendre le code. J'ai eu ce problème et la façon dont je l'ai fait était de faire un appel synchrone SJAX au serveur, et le serveur exécute effectivement sleep ou fait une activité avant de retourner et pendant tout ce temps, le js attend.

Eg de Sync AJAX : http://www.hunlock.com/blogs/Snippets%3A%5FSynchronous%5FAJAX

2 votes

SJAX est généralement un signe de mauvaise conception et j'encourage les novices à l'éviter jusqu'à ce qu'ils comprennent ses implications.

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