Rufen Sie eine Funktion auf, nachdem die vorherige Funktion abgeschlossen wurde

178

Ich habe den folgenden JavaScript-Code:

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

Wie kann ich sicherstellen, dass der function2Aufruf erst nach function1Abschluss erfolgt?

Rrryyyaaannn
quelle
24
ist function1eine asynchrone Operation?
LiraNuna
8
Yads, wenn Funktion1 Animationen enthält, wird Funktion2 ausgeführt, während die Animationen von Funktion1 noch ausgeführt werden. Wie würden Sie function2 warten lassen, bis alles, was in function1 gestartet wurde, vollständig erledigt ist?
Trusktr
Kann mir jemand erklären, warum etwas getan werden muss, um sicherzustellen, dass Funktion2 nicht aufgerufen wird, bis Funktion1 abgeschlossen ist? Hier findet keine asynchrone Operation statt, sodass Funktion2 erst ausgeführt wird, wenn Funktion1 sowieso abgeschlossen ist, da diese Operation synchron ist.
Aaron

Antworten:

204

Geben Sie einen anonymen Rückruf an und lassen Sie function1 ihn akzeptieren:

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


function function1(param, callback) {
  ...do stuff
  callback();
} 
Mike Robinson
quelle
11
+1, aber wenn er 1,5 verwendet, kann er einfach das neue Deferreds-Muster verwenden
Philwinkle
Vielen Dank für die schnelle Antwort (en). Ja, function1 führt nur eine einfache asynchrone Aufgabe aus. Ich glaube, Sie haben Recht damit, dass ich einen Rückruf erstellen muss - oder einen Blick auf dieses von philwinkle erwähnte verzögerte Geschäft werfen muss. Nochmals vielen Dank, Jungs. Ich werde etwas schlafen und mich morgen früh wieder darum kümmern.
Rrryyyaaannn
13
Diese Antwort ist nicht die Lösung. Hier ist der Beweis, dass es nicht funktioniert: jsfiddle.net/trusktr/M2h6e
trusktr
12
@ MikeRobinson Hier ist das Problem: Was ist, wenn das ...do stuffDinge enthält, die asynchron sind? Function2 wird immer noch nicht ausgelöst, wenn dies erwartet wird. Das Beispiel in meinem obigen Kommentar zeigt, dass die foo()Funktion , da sie asynchronen Code bar()enthält, ihren Inhalt nicht auslöst, nachdem foo()der Inhalt ausgeführt wurde, selbst wenn $.when().then()sie nacheinander aufgerufen wird. Diese Antwort ist nur gültig, wenn (und nur wenn) alle Inhalte ...do stuffin Ihrem Code streng synchron sind.
trusktr
1
@trusktr In diesem Fall müssen Sie den ersten Rückruf an die n- te Ebene des asynchronen Anrufs weiterleiten und dann auslösen, callBack()nachdem alle n asynchronen Aufrufe ausgeführt wurden. Da OP nicht erwähnt hat, was er in der Funktion tut, wird davon ausgegangen, dass es sich um einen asynchronen Aufruf auf einer Ebene handelt.
GoodSp33d
96

Wenn Sie jQuery 1.5 verwenden, können Sie das neue Deferreds-Muster verwenden:

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

Bearbeiten: Aktualisierter Blog-Link:

Rebecca Murphy hatte hier eine großartige Zusammenfassung: http://rmurphey.com/blog/2010/12/25/deferreds-coming-to-jquery/

philwinkle
quelle
2
Aber ist das ein funktionierendes Beispiel? Was wird zurückgestellt? Sie führen Funktion1 und Funktion2 sofort aus. (Ich bin nicht der ursprüngliche Kommentator.)
user113716
10
Das funktioniert bei mir überhaupt nicht. Funktion1 und Funktion2 werden beide genau zur gleichen Zeit ausgeführt (wie vom menschlichen Auge beobachtet). Hier ist das winzige Beispiel: jsfiddle.net/trusktr/M2h6e
trusktr
1
Rebecca Murphys Artikel wurde vor der Einführung von Defferds in jQuery geschrieben und verwendet Beispiele, die darauf basieren, wie der Autor glaubt, dass er implementiert wird. Bitte besuchen Sie die Website von jQuery, um zu sehen, wie diese Funktion tatsächlich verwendet wird: api.jquery.com/jQuery.Deferred
Spencer Williams
1
Wollen Sie nicht, auch mit jQuery 1.5 oder etwas Modernem $.when(function1).then(function2);(ohne die Klammern, die die Funktion aufrufen)?
Teepeemm
1
Lesen Sie diese Kommentare einige Jahre zu spät. function1sollte ein Versprechen zurückgeben. In diesem Fall muss es sich um die tatsächlich ausgeführte Funktion handeln, nicht um die Referenz. Wenn resolvedieses Versprechen aufgerufen wird, function2wird es ausgeführt. Dies lässt sich leicht erklären, wenn man davon ausgeht, dass function1ein Ajax-Aufruf vorliegt. Der doneRückruf würde dann das Versprechen lösen. Ich hoffe das ist klar.
Philwinkle
46

Versuche dies :

function method1(){
   // some code

}

function method2(){
   // some code
}

$.ajax({
   url:method1(),
   success:function(){
   method2();
}
})
TuanTruong
quelle
37

Diese Antwort verwendet promiseseine JavaScript-Funktion des ECMAScript 6Standards. Wenn Ihre Zielplattform dies nicht unterstützt promises, füllen Sie sie mit PromiseJs .

Versprechen sind eine neue (und viel bessere) Möglichkeit, asynchrone Operationen in JavaScript zu handhaben:

$('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
    });
} 

Dies mag für dieses einfache Beispiel als erheblicher Aufwand erscheinen, für komplexeren Code ist es jedoch weitaus besser als die Verwendung von Rückrufen. Sie können mehrere asynchrone Aufrufe einfach mit mehreren thenAnweisungen verketten:

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

Sie können jQuery-Verzögerungen auch einfach umbrechen (die von $.ajaxAufrufen zurückgegeben werden):

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

Wie @charlietfl feststellte, implementiert das jqXHRvon zurückgegebene Objekt $.ajax()die PromiseSchnittstelle. Es ist also eigentlich nicht notwendig, es in ein zu wickeln Promise, es kann direkt verwendet werden:

$.ajax(...params...).then(function(result) {
    //whatever you want to do after the request
});
Domysee
quelle
Promise.resolveDas Einwickeln $.ajaxist ein Anti-Muster, da es ein $.ajaxvielversprechendes Versprechen
zurückgibt
@charlietfl, aber es ist keine tatsächliche Promise, es implementiert nur die Schnittstelle. Ich bin nicht sicher, wie es sich verhält, wenn ein Real Promisean seine thenMethode übergeben wird, oder was a Promise.alltun würde, wenn a jqXHRund a Promisean ihn übergeben werden. Dafür muss ich weitere Tests durchführen. Aber danke für den Hinweis, ich werde ihn der Antwort hinzufügen!
Domysee
In jQuery Version 3+ ist Promises A + konform. Unabhängig davon $.ajax.thenist nicht neu
charlietfl
21

Oder Sie können ein benutzerdefiniertes Ereignis auslösen, wenn eine Funktion abgeschlossen ist, und es dann an das Dokument binden:

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

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

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

Mit dieser Methode kann die Funktion 'b' erst NACH der Funktion 'a' ausgeführt werden, da der Trigger nur vorhanden ist, wenn die Ausführung der Funktion a abgeschlossen ist.

de Raad
quelle
"Ab jQuery 1.7 ist die .on () -Methode die bevorzugte Methode zum Anhängen von Ereignishandlern an ein Dokument." api.jquery.com/bind
CodeVirtuoso
3

Dies hängt davon ab, was function1 tut.

Wenn Funktion1 ein einfaches synchrones Javascript ausführt, z. B. das Aktualisieren eines Div-Werts oder ähnliches, wird Funktion2 nach Abschluss von Funktion1 ausgelöst.

Wenn function1 einen asynchronen Aufruf ausführt, z. B. einen AJAX-Aufruf, müssen Sie eine "Rückruf" -Methode erstellen (die meisten Ajax-APIs haben einen Rückruffunktionsparameter). Rufen Sie dann function2 im Rückruf auf. z.B:

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

function MyCallback(result)
{
  function2(result);
}
Russell
quelle
3

Sie können es so machen

$.when(funtion1()).then(function(){
    funtion2();
})
Jay Momaya
quelle
2

Wenn Methode 1 nach Methode 2, 3, 4 ausgeführt werden muss. Das folgende Codefragment kann die Lösung hierfür sein, indem das Objekt "Zurückgestellt" in JavaScript verwendet wird.

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
quelle
0

Wenn function1 eine Synchronisierungsfunktion ist, die Sie in eine asynchrone Funktion umwandeln möchten, da die Ausführung einige Zeit in Anspruch nimmt und Sie keine Kontrolle darüber haben, um einen Rückruf hinzuzufügen:

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 ();

Die Ausgabe wird sein:

Click handled

... und nach 2 Sekunden:

This is function 1
This is function 2

Dies funktioniert, weil durch das Aufrufen von window.setTimeout () der JS-Runtine-Taskschleife eine Aufgabe hinzugefügt wird, wie dies bei einem asynchronen Aufruf der Fall ist, und weil das Grundprinzip der "Run-to-Completion" der JS-Laufzeit sicherstellt, dass onClick () wird nie unterbrochen, bevor es endet.

Beachten Sie, dass dies so lustig wie der Code schwer zu verstehen sein mag ...

StashOfCode
quelle