Appeler une fonction une fois la fonction précédente terminée

187

J'ai le code JavaScript suivant:

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable);
        function2(someOtherVariable);
    }
    else {
        doThis(someVariable);
    }
});

Comment puis-je m'assurer qu'il function2n'est appelé qu'après avoir function1terminé?

Rrryyyaaannn
la source
24
est l' function1exécution d' une opération asynchrone?
LiraNuna
8
Yads, si function1 contient des animations, function2 sera exécutée pendant que les animations de function1 sont toujours en cours. Comment feriez-vous que function2 attende que tout a commencé dans function1 soit complètement terminé?
trusktr
Quelqu'un peut-il m'expliquer pourquoi il faut faire quelque chose pour s'assurer que function2 n'est pas invoquée tant que function1 n'est pas terminée? Il n'y a pas d'opération asynchrone qui se produit ici, donc function2 ne fonctionnera pas tant que function1 ne sera pas terminée de toute façon car cette opération est synchrone.
Aaron

Réponses:

214

Spécifiez un rappel anonyme et faites en sorte que function1 l'accepte:

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable, function() {
          function2(someOtherVariable);
        });
    }
    else {
        doThis(someVariable);
    }
});


function function1(param, callback) {
  ...do stuff
  callback();
} 
Mike Robinson
la source
11
+1 mais s'il utilise 1.5, il peut simplement utiliser le nouveau modèle
Différés
Merci pour les réponses rapides). Oui, function1 n'effectue qu'une simple tâche asynchrone, donc je pense que vous avez raison en ce sens que je vais devoir créer un rappel - ou jeter un œil à cette activité différée mentionnée par philwinkle. Merci encore les gars. Je vais dormir un peu et m'attaquer à nouveau demain matin.
Rrryyyaaannn
13
Cette réponse n'est pas la solution. Voici la preuve que cela ne fonctionne pas: jsfiddle.net/trusktr/M2h6e
trusktr
12
@MikeRobinson Voici le problème: et si le ...do stuffcontient des éléments asynchrones? Function2 ne se déclenchera toujours pas comme prévu. L'exemple dans mon commentaire ci-dessus montre que parce que la foo()fonction contient du code asynchrone, bar()ne déclenche pas son contenu une fois que foo()le contenu de son exécution est terminé, même en utilisant $.when().then()pour les appeler l'un après l'autre. Cette réponse n'est valide que si (et seulement si) tous les éléments ...do stuffde votre code sont strictement synchrones.
trusktr
1
@trusktr Dans ce cas, vous devez continuer à passer le premier rappel au n ème niveau de l'appel asynchrone, puis déclencher callBack()après que tous les n appels asynchrones ont été effectués. Puisque OP n'a pas mentionné ce qu'il fait dans la fonction, il l'a supposé comme un appel asynchrone à un niveau.
GoodSp33d
98

Si vous utilisez jQuery 1.5, vous pouvez utiliser le nouveau modèle Deferreds:

$('a.button').click(function(){
    if(condition == 'true'){
        $.when(function1()).then(function2());
    }
    else {
        doThis(someVariable);
    }
});

Edit: Lien de blog mis à jour:

Rebecca Murphy a fait un excellent article à ce sujet ici: http://rmurphey.com/blog/2010/12/25/deferreds-coming-to-jquery/

Philwinkle
la source
2
Mais est-ce un exemple concret? Qu'est-ce qui est reporté? Vous exécutez immédiatement function1 et function2. (Je ne suis pas le commentateur d'origine.)
user113716
11
Cela ne fonctionne pas du tout pour moi. function1 et function2 sont tous deux exécutés exactement au même moment (comme observable par l'œil humain). Voici le petit exemple: jsfiddle.net/trusktr/M2h6e
trusktr
1
La rédaction de Rebecca Murphy a eu lieu avant l'introduction de Defferds à jQuery et utilise des exemples basés sur la manière dont l'auteur pense qu'il sera implémenté. Veuillez visiter le site de jQuery pour voir comment utiliser réellement cette fonctionnalité: api.jquery.com/jQuery.Deferred
Spencer Williams
1
Même avec jQuery 1.5 ou quelque chose de moderne, ne voulez-vous pas $.when(function1).then(function2);(sans les parenthèses qui appellent la fonction)?
Teepeemm
1
Lire ces commentaires avec quelques années de retard. function1devrait retourner une promesse. Dans ce cas, il doit s'agir de la fonction réellement exécutée et non de la référence. Quand resolveest appelé sur cette promesse function2est alors exécuté. Cela s'explique facilement en supposant qu'il function1a un appel ajax. Le donerappel résoudrait alors la promesse. J'espère que c'est clair.
philwinkle
49

Essaye ça :

function method1(){
   // some code

}

function method2(){
   // some code
}

$.ajax({
   url:method1(),
   success:function(){
   method2();
}
})
TuanTruong
la source
39

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

Les promesses sont une nouvelle (et bien meilleure) façon de gérer les opérations asynchrones en JavaScript:

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable).then(function() {
            //this function is executed after function1
            function2(someOtherVariable);
        });
    }
    else {
        doThis(someVariable);
    }
});


function function1(param, callback) {
    return new Promise(function (fulfill, reject){
        //do stuff
        fulfill(result); //if the action succeeded
        reject(error); //if the action did not succeed
    });
} 

Cela peut sembler une surcharge importante pour cet exemple simple, mais pour un code plus complexe, c'est bien mieux que d'utiliser des rappels. Vous pouvez facilement chaîner plusieurs appels asynchrones à l'aide de plusieurs theninstructions:

function1(someVariable).then(function() {
    function2(someOtherVariable);
}).then(function() {
    function3();
});

Vous pouvez également encapsuler facilement les reports jQuery (qui sont renvoyés par les $.ajaxappels):

Promise.resolve($.ajax(...params...)).then(function(result) {
    //whatever you want to do after the request
});

Comme @charlietfl l'a noté, l' jqXHRobjet renvoyé par $.ajax()implémente l' Promiseinterface. Il n'est donc pas nécessaire de l'envelopper dans un Promise, il peut être utilisé directement:

$.ajax(...params...).then(function(result) {
    //whatever you want to do after the request
});
Domysee
la source
Promise.resolvele wrapping $.ajaxest anti-pattern puisque $.ajaxretourne la promesse
durable
@charlietfl mais ce n'est pas un réel Promise, il implémente simplement l'interface. Je ne sais pas comment il se comporte lors du passage d'un réel Promiseà sa thenméthode, ni ce que Promise.allferait a si a jqXHRet a lui Promisesont passés. Pour cela, je dois faire des tests supplémentaires. Mais merci pour l'indice, je l'ajouterai à la réponse!
Domysee
En fait, la version 3+ de jQuery est conforme aux promesses A +. Irregardless $.ajax.thenis not new
charlietfl
21

Ou vous pouvez déclencher un événement personnalisé lorsqu'une fonction se termine, puis le lier au document:

function a() {
    // first function code here
    $(document).trigger('function_a_complete');
}

function b() {
    // second function code here
}

$(document).bind('function_a_complete', b);

En utilisant cette méthode, la fonction 'b' ne peut exécuter qu'APRÈS la fonction 'a', car le déclencheur n'existe que lorsque la fonction a est terminée.

de Raad
la source
"Depuis jQuery 1.7, la méthode .on () est la méthode préférée pour attacher des gestionnaires d'événements à un document." api.jquery.com/bind
CodeVirtuoso
5

tu peux le faire comme ça

$.when(funtion1()).then(function(){
    funtion2();
})
Jay Momaya
la source
3

Cela dépend de ce que fait function1.

Si function1 fait du javascript synchrone simple, comme la mise à jour d'une valeur div ou quelque chose, alors function2 se déclenchera après que function1 soit terminée.

Si function1 effectue un appel asynchrone, tel qu'un appel AJAX, vous devrez créer une méthode de "rappel" (la plupart des API ajax ont un paramètre de fonction de rappel). Appelez ensuite function2 dans le callback. par exemple:

function1()
{
  new AjaxCall(ajaxOptions, MyCallback);
}

function MyCallback(result)
{
  function2(result);
}
Russell
la source
3

Si la méthode 1 doit être exécutée après la méthode 2, 3, 4. L'extrait de code suivant peut être la solution pour cela en utilisant l'objet Deferred en JavaScript.

function method1(){
  var dfd = new $.Deferred();
     setTimeout(function(){
     console.log("Inside Method - 1"); 
     method2(dfd);	 
    }, 5000);
  return dfd.promise();
}

function method2(dfd){
  setTimeout(function(){
   console.log("Inside Method - 2"); 
   method3(dfd);	
  }, 3000);
}

function method3(dfd){
  setTimeout(function(){
   console.log("Inside Method - 3"); 	
   dfd.resolve();
  }, 3000);
}

function method4(){   
   console.log("Inside Method - 4"); 	
}

var call = method1();

$.when(call).then(function(cb){
  method4();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Sahadeb Patro
la source
0

Si function1 est une fonction de synchronisation que vous souhaitez transformer en une fonction asynchrone, car elle prend du temps à se terminer et vous n'avez aucun contrôle dessus pour ajouter un rappel:

function function1 (someVariable) {
    var date = Date.now ();
    while (Date.now () - date < 2000);      // function1 takes some time to complete
    console.log (someVariable);
}
function function2 (someVariable) {
    console.log (someVariable);
}
function onClick () {
    window.setTimeout (() => { function1 ("This is function1"); }, 0);
    window.setTimeout (() => { function2 ("This is function2"); }, 0);
    console.log ("Click handled");  // To show that the function will return before both functions are executed
}
onClick ();

La sortie sera:

Click handled

... et après 2 secondes:

This is function 1
This is function 2

Cela fonctionne parce que l'appel de window.setTimeout () ajoutera une tâche à la boucle de tâches d'exécution JS, ce qui correspond à un appel asynchrone, et parce que le principe de base du "run-to-completion" du runtime JS garantit que onClick () n'est jamais interrompu avant la fin.

Notez que c'est aussi drôle que cela puisse être le code difficile à comprendre ...

StashOfCode
la source