web-dev-qa-db-fra.com

Comment dois-je appeler 3 fonctions pour les exécuter les unes après les autres?

Si j’ai besoin d’appeler cette fonction l’une après l’autre,

$('#art1').animate({'width':'1000px'},1000);        
$('#art2').animate({'width':'1000px'},1000);        
$('#art3').animate({'width':'1000px'},1000);        

Je sais que dans jQuery je pourrais faire quelque chose comme:

$('#art1').animate({'width':'1000px'},1000,'linear',function(){
    $('#art2').animate({'width':'1000px'},1000,'linear',function(){
        $('#art3').animate({'width':'1000px'},1000);        
    });        
});        

Mais supposons que je n'utilise pas jQuery et que je souhaite appeler:

some_3secs_function(some_value);        
some_5secs_function(some_value);        
some_8secs_function(some_value);        

Comment appeler ces fonctions pour exécuter some_3secs_function, APRES la fin de cet appel, puis exécuter some_5secs_function et APRES la fin de cet appel, puis appeler some_8secs_function?

PDATE:

Cela ne fonctionne toujours pas:

(function(callback){
    $('#art1').animate({'width':'1000px'},1000);
    callback();
})((function(callback2){
    $('#art2').animate({'width':'1000px'},1000);
    callback2();
})(function(){
    $('#art3').animate({'width':'1000px'},1000);
}));

Trois animations commencent en même temps

Où est mon erreur?

133
texai

En Javascript, il existe des fonctions synchrones et asynchrones .

Fonctions synchrones

La plupart des fonctions en Javascript sont synchrones. Si vous deviez appeler plusieurs fonctions synchrones à la suite

doSomething();
doSomethingElse();
doSomethingUsefulThisTime();

ils exécuteront dans l'ordre. doSomethingElse ne démarrera pas tant que doSomething ne sera pas terminé. doSomethingUsefulThisTime, à son tour, ne commencera pas tant que doSomethingElse ne sera pas terminé.

Fonctions asynchrones

La fonction asynchrone ne s’attendra cependant pas. Voyons le même exemple de code que ci-dessus, cette fois en supposant que les fonctions sont asynchrones

doSomething();
doSomethingElse();
doSomethingUsefulThisTime();

Les fonctions seront initialisées dans l’ordre, mais elles seront toutes exécutées à peu près en même temps. Vous ne pouvez pas toujours prédire laquelle finira en premier: celle qui prendra le moins de temps possible sera exécutée en premier.

Mais parfois, vous souhaitez que les fonctions asynchrones s'exécutent dans l'ordre, et parfois, les fonctions synchrones s'exécutent de manière asynchrone. Heureusement, cela est possible avec les rappels et les délais d'attente, respectivement.

Rappels

Supposons que nous souhaitons exécuter trois fonctions asynchrones dans l'ordre, some_3secs_function, some_5secs_function et some_8secs_function.

Comme les fonctions peuvent être passées sous forme d'arguments en Javascript, vous pouvez passer une fonction en tant que rappel à exécuter une fois la fonction terminée.

Si on crée les fonctions comme ça

function some_3secs_function(value, callback){
  //do stuff
  callback();
}

alors vous pouvez appeler ensuite dans l'ordre, comme ceci:

some_3secs_function(some_value, function() {
  some_5secs_function(other_value, function() {
    some_8secs_function(third_value, function() {
      //All three functions have completed, in order.
    });
  });
});

Délais d'attente

En Javascript, vous pouvez indiquer à une fonction de s'exécuter après un certain délai (en millisecondes). Cela peut en effet rendre les fonctions synchrones se comporter de manière asynchrone.

Si nous avons trois fonctions synchrones, nous pouvons les exécuter de manière asynchrone à l'aide de la fonction setTimeout.

setTimeout(doSomething, 10);
setTimeout(doSomethingElse, 10);
setTimeout(doSomethingUsefulThisTime, 10);

Ceci est cependant un peu moche et viole le principe principe DRY[Wikipédia] . Nous pourrions nettoyer cela un peu en créant une fonction qui accepte un tableau de fonctions et un délai d'attente.

function executeAsynchronously(functions, timeout) {
  for(var i = 0; i < functions.length; i++) {
    setTimeout(functions[i], timeout);
  }
}

Cela peut s'appeler comme suit:

executeAsynchronously(
    [doSomething, doSomethingElse, doSomethingUsefulThisTime], 10);

En résumé, si vous avez des fonctions asynchrones que vous souhaitez exécuter de manière syncrone, utilisez des rappels, et si vous avez des fonctions synchrones que vous souhaitez exécuter de manière asynchrone, utilisez des délais d'attente.

224
Peter Olson

Cette réponse utilise promises , une fonctionnalité JavaScript de la norme ECMAScript 6. Si votre plate-forme cible ne prend pas en charge promises, remplissez-la avec PromiseJs .

Regardez ma réponse ici Attendez qu'une fonction avec des animations soit terminée avant d'exécuter une autre fonction si vous souhaitez utiliser des animations jQuery.

Voici à quoi ressemblerait votre code avec ES6 Promises et jQuery animations.

Promise.resolve($('#art1').animate({ 'width': '1000px' }, 1000).promise()).then(function(){
    return Promise.resolve($('#art2').animate({ 'width': '1000px' }, 1000).promise());
}).then(function(){
    return Promise.resolve($('#art3').animate({ 'width': '1000px' }, 1000).promise());
});

Les méthodes normales peuvent également être encapsulées dans Promises.

new Promise(function(fulfill, reject){
    //do something for 5 seconds
    fulfill(result);
}).then(function(result){
    return new Promise(function(fulfill, reject){
        //do something for 5 seconds
        fulfill(result);
    });
}).then(function(result){
    return new Promise(function(fulfill, reject){
        //do something for 8 seconds
        fulfill(result);
    });
}).then(function(result){
    //do something with the result
});

La méthode then est exécutée dès que Promise est terminé. Normalement, la valeur de retour de function passée à then est transmise à la suivante comme résultat.

Mais si un Promise est renvoyé, la fonction then suivante attend jusqu'à ce que Promise ait fini de s'exécuter et en reçoive les résultats (la valeur transmise à fulfill).

32
Domysee

Il semble que vous n'appréciez pas pleinement la différence entre l'exécution de la fonction synchrone et asynchrone.

Le code que vous avez fourni dans votre mise à jour immédiatement exécute chacune de vos fonctions de rappel, ce qui démarre immédiatement une animation. Cependant, les animations s'exécutent de manière asyncronante . Cela fonctionne comme ceci:

  1. Effectuer une étape dans l'animation
  2. Appelez setTimeout avec une fonction contenant l'étape d'animation suivante et un délai
  3. Un peu de temps passe
  4. Le rappel donné à setTimeout s'exécute
  5. Retourner à l'étape 1

Cela continue jusqu'à la fin de la dernière étape de l'animation. Entre-temps, vos fonctions synchrones sont terminées depuis longtemps. En d'autres termes, votre appel à la fonction animate ne prend pas réellement 3 secondes. L'effet est simulé avec des retards et des rappels.

Ce dont vous avez besoin est un file d'attente. En interne, jQuery met en file d'attente les animations, n'exécutant votre rappel qu'une fois l'animation correspondante terminée. Si votre rappel démarre alors une autre animation, elles sont exécutées en séquence.

Dans le cas le plus simple, cela équivaut à ce qui suit:

window.setTimeout(function() {
    alert("!");
    // set another timeout once the first completes
    window.setTimeout(function() {
        alert("!!");
    }, 1000);
}, 3000); // longer, but first

Voici une fonction générale de bouclage asynchrone. Il appellera les fonctions données dans l’ordre, en attendant le nombre de secondes spécifié.

function loop() {
    var args = arguments;
    if (args.length <= 0)
        return;
    (function chain(i) {
        if (i >= args.length || typeof args[i] !== 'function')
            return;
        window.setTimeout(function() {
            args[i]();
            chain(i + 1);
        }, 2000);
    })(0);
}    

Usage:

loop(
  function() { alert("sam"); }, 
  function() { alert("sue"); });

Vous pouvez évidemment modifier ceci pour prendre des temps d’attente configurables ou pour exécuter immédiatement la première fonction ou pour arrêter de l’exécuter lorsqu'une fonction de la chaîne renvoie false ou à apply les fonctions d’un contexte spécifié ou autre. va peut-être avoir besoin de.

20
Wayne Burkett

Je crois que la bibliothèque asynchrone vous fournira un moyen très élégant de le faire. Alors que les promesses et les rappels peuvent être un peu difficiles à jongler, async peut donner des schémas ordonnés pour rationaliser votre processus de pensée. Pour exécuter des fonctions en série, vous devez les placer en mode asynchrone cascade . Dans le jargon asynchrone, chaque fonction est appelée une task qui prend des arguments et une callback; qui est la fonction suivante de la séquence. La structure de base ressemblerait à quelque chose comme:

async.waterfall([
  // A list of functions
  function(callback){
      // Function no. 1 in sequence
      callback(null, arg);
  },
  function(arg, callback){
      // Function no. 2 in sequence
      callback(null);
  }
],    
function(err, results){
   // Optional final callback will get results for all prior functions
});

Je viens d'essayer d'expliquer brièvement la structure ici. Lisez la cascade guide pour plus d'informations, c'est assez bien écrit.

10
Ali

vos fonctions doivent prendre une fonction de rappel, qui est appelée à la fin.

function fone(callback){
...do something...
callback.apply(this,[]);

}

function ftwo(callback){
...do something...
callback.apply(this,[]);
}

alors l'utilisation serait comme:

fone(function(){
  ftwo(function(){
   ..ftwo done...
  })
});
8
user406905
asec=1000; 

setTimeout('some_3secs_function("somevalue")',asec*3);
setTimeout('some_5secs_function("somevalue")',asec*5);
setTimeout('some_8secs_function("somevalue")',asec*8);

Je n'entrerai pas dans une discussion approfondie de setTimeout ici, mais:

  • dans ce cas, j'ai ajouté le code à exécuter en tant que chaîne. C'est le moyen le plus simple de passer une variable var à votre fonction setTimeout, mais les puristes s'en plaindront.
  • vous pouvez également passer un nom de fonction sans guillemets, mais aucune variable ne peut être transmise.
  • votre code n'attend pas le déclenchement de setTimeout.
  • Celui-ci peut être difficile à comprendre: à cause du point précédent, si vous transmettez une variable de votre fonction appelante, cette variable n’existera plus au moment où le délai d’attente se déclenche - la fonction appelante s’est exécutée et c’est vars partis.
  • On m'a appris à utiliser des fonctions anonymes pour contourner tout cela, mais il pourrait bien y avoir un meilleur moyen,
4
Trass Vasston

Puisque vous l'avez taguée avec javascript, j'utiliserais un contrôle de minuterie puisque les noms de vos fonctions sont 3, 5 et 8 secondes. Alors lancez votre chronomètre, 3 secondes avant, appelez le premier, 5 secondes en appelez le second, 8 secondes en appelez le troisième, puis lorsque c'est terminé, arrêtez le chronomètre.

Normalement, en Javascript, ce que vous avez est correct car les fonctions sont exécutées les unes après les autres, mais comme il semble que vous essayez de réaliser une animation chronométrée, une minuterie serait votre meilleur choix.

3
Sorean
//sample01
(function(_){_[0]()})([
        function(){$('#art1').animate({'width':'10px'},100,this[1].bind(this))},
        function(){$('#art2').animate({'width':'10px'},100,this[2].bind(this))},
        function(){$('#art3').animate({'width':'10px'},100)},
])

//sample02
(function(_){_.next=function(){_[++_.i].apply(_,arguments)},_[_.i=0]()})([
        function(){$('#art1').animate({'width':'10px'},100,this.next)},
        function(){$('#art2').animate({'width':'10px'},100,this.next)},
        function(){$('#art3').animate({'width':'10px'},100)},
]);

//sample03
(function(_){_.next=function(){return _[++_.i].bind(_)},_[_.i=0]()})([
        function(){$('#art1').animate({'width':'10px'},100,this.next())},
        function(){$('#art2').animate({'width':'10px'},100,this.next())},
        function(){$('#art3').animate({'width':'10px'},100)},
]);
2
yuuya

Vous pouvez également utiliser les promesses de cette manière:

    some_3secs_function(this.some_value).then(function(){
       some_5secs_function(this.some_other_value).then(function(){
          some_8secs_function(this.some_other_other_value);
       });
    });

Vous devez rendre some_value global pour y accéder de l'intérieur du fichier .then

Vous pouvez également renvoyer, à partir de la fonction externe, la valeur que la fonction interne utiliserait, comme suit:

    one(some_value).then(function(return_of_one){
       two(return_of_one).then(function(return_of_two){
          three(return_of_two);
       });
    });
1
Cuzox

J'utilise une fonction 'waitUntil' basée sur setTimeout de javascript

/*
    funcCond : function to call to check whether a condition is true
    readyAction : function to call when the condition was true
    checkInterval : interval to poll <optional>
    timeout : timeout until the setTimeout should stop polling (not 100% accurate. It was accurate enough for my code, but if you need exact milliseconds, please refrain from using Date <optional>
    timeoutfunc : function to call on timeout <optional>
*/
function waitUntil(funcCond, readyAction, checkInterval, timeout, timeoutfunc) {
    if (checkInterval == null) {
        checkInterval = 100; // checkinterval of 100ms by default
    }
    var start = +new Date(); // use the + to convert it to a number immediatly
    if (timeout == null) {
        timeout = Number.POSITIVE_INFINITY; // no timeout by default
    }
    var checkFunc = function() {
        var end = +new Date(); // rough timeout estimations by default

        if (end-start > timeout) {
            if (timeoutfunc){ // if timeout function was defined
                timeoutfunc(); // call timeout function
            }
        } else {
            if(funcCond()) { // if condition was met
                readyAction(); // perform ready action function
            } else {
                setTimeout(checkFunc, checkInterval); // else re-iterate
            }
        }
    };
    checkFunc(); // start check function initially
};

Cela fonctionnerait parfaitement si vos fonctions définissaient une certaine condition sur true, que vous seriez en mesure d’examiner. De plus, il comporte des délais d'expiration, qui vous offrent des alternatives au cas où votre fonction n'agirait pas (même dans le délai imparti. Pensez aux commentaires des utilisateurs!).

par exemple

doSomething();
waitUntil(function() { return doSomething_value===1;}, doSomethingElse);
waitUntil(function() { return doSomethingElse_value===1;}, doSomethingUseful);

Notes

La date provoque des estimations approximatives de délai d'attente. Pour plus de précision, passez à des fonctions telles que console.time (). Notez que Date offre une meilleure prise en charge multi-navigateurs et héritée. Si vous n'avez pas besoin de mesures exactes en millisecondes; ne vous embêtez pas, ou bien emballez-le et proposez console.time () lorsque le navigateur le supporte

0