Wywołaj funkcję po zakończeniu poprzedniej funkcji

179

Mam następujący kod JavaScript:

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

Jak mogę się upewnić, że function2wywołanie nastąpi dopiero po function1zakończeniu?

Rrryyyaaannn
źródło
24
jest function1wykonywanie operacji asynchronicznej?
LiraNuna,
8
Tak, jeśli funkcja 1 zawiera animacje, funkcja 2 zostanie wykonana, gdy animacje funkcji 1 będą nadal działać. Jak sprawiłbyś, żeby funkcja 2 czekała, aż wszystko uruchomione w funkcji 1 zostanie całkowicie wykonane?
trusktr
Czy ktoś może mi wyjaśnić, dlaczego należy coś zrobić, aby funkcja 2 nie została wywołana, dopóki funkcja 1 nie zostanie ukończona? Nie dzieje się tutaj żadna operacja asynchroniczna, więc funkcja 2 nie uruchomi się, dopóki funkcja 1 nie zostanie ukończona, ponieważ operacja ta jest synchroniczna.
Aaron,

Odpowiedzi:

206

Podaj anonimowe wywołanie zwrotne i spraw, aby funkcja1 zaakceptowała je:

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


function function1(param, callback) {
  ...do stuff
  callback();
} 
Mike Robinson
źródło
11
+1, ale jeśli używa 1.5, może po prostu użyć nowego wzoru
odroczenia
Dziękuje za szybkie odpowiedzi). Tak, funkcja 1 wykonuje po prostu proste zadanie asynchroniczne, więc uważam, że masz rację, że będę musiał utworzyć wywołanie zwrotne - lub spójrz na ten odroczony biznes, o którym wspominał philwinkle. Jeszcze raz dziękuję. Prześpię się i poradzę sobie z tym rano.
Rrryyyaaannn
13
Ta odpowiedź nie jest rozwiązaniem. Oto dowód, że to nie działa: jsfiddle.net/trusktr/M2h6e
trusktr
12
@MikeRobinson Oto problem: co, jeśli ...do stuffzawiera rzeczy asynchroniczne? Funkcja 2 nadal nie uruchomi się zgodnie z oczekiwaniami. Przykład w moim powyższym komentarzu pokazuje, że ponieważ foo()funkcja zawiera kod asynchroniczny, bar()nie uruchamia swojej zawartości po zakończeniu foo()wykonywania zawartości, nawet używając $.when().then()wywoływania ich jedna po drugiej. Ta odpowiedź jest poprawna tylko wtedy, gdy (i tylko wtedy) wszystkie elementy ...do stuffw twoim kodzie są ściśle synchroniczne.
trusktr
1
@trusktr W takim przypadku musisz przekazywać pierwsze wywołanie zwrotne do n- tego poziomu asynchronicznego wywołania, a następnie uruchamiać callBack()po wykonaniu wszystkich n wywołań asynchronicznych. Ponieważ OP nie wspomniał o tym, co robi w funkcji, przyjęto, że jest to wywołanie asynchroniczne na jednym poziomie.
GoodSp33d
96

Jeśli używasz jQuery 1.5, możesz użyć nowego wzorca Deferreds:

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

Edytuj: Zaktualizowany link do bloga:

Rebecca Murphy świetnie napisała na ten temat tutaj: http://rmurphey.com/blog/2010/12/25/deferreds-coming-to-jquery/

philwinkle
źródło
2
Ale czy to działa przykład? Co jest odraczane? Natychmiast uruchamiasz funkcję 1 i funkcję 2. (Nie jestem oryginalnym komentatorem.)
user113716
10
To w ogóle nie działa dla mnie. zarówno funkcja 1, jak i funkcja 2 są wykonywane dokładnie w tym samym czasie (co można zaobserwować ludzkim okiem). Oto mały przykład: jsfiddle.net/trusktr/M2h6e
trusktr
1
Pisanie Rebeki Murphy było przed wprowadzeniem Defferds do jQuery i wykorzystuje przykłady oparte na tym, jak pisarz myśli, że zostanie zaimplementowany. Odwiedź stronę jQuery, aby zobaczyć, jak faktycznie korzystać z tej funkcji: api.jquery.com/jQuery.Deferred
Spencer Williams
1
Nawet z jQuery 1.5 lub czymś nowoczesnym, nie chcesz $.when(function1).then(function2);(bez nawiasów wywołujących tę funkcję)?
Teepeemm
1
Czytanie tych komentarzy kilka lat później. function1powinien zwrócić obietnicę. W takim przypadku musi to być faktycznie wykonywana funkcja, a nie odwołanie. Kiedy resolvezostaje przywołana ta obietnica, function2zostaje wykonana. Można to łatwo wytłumaczyć, zakładając, że function1ma wywołanie ajax. doneZwrotna będzie wtedy rozwiązać obietnicy. Mam nadzieję, że to jasne.
philwinkle
46

Spróbuj tego :

function method1(){
   // some code

}

function method2(){
   // some code
}

$.ajax({
   url:method1(),
   success:function(){
   method2();
}
})
TuanTruong
źródło
37

Ta odpowiedź używa standardowej promisesfunkcji JavaScript ECMAScript 6. Jeśli twoja platforma docelowa nie obsługuje promises, wypełnij ją PromiseJs .

Obietnice to nowy (i znacznie lepszy) sposób obsługi operacji asynchronicznych w 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
    });
} 

Może to wydawać się znaczącym narzutem w tym prostym przykładzie, ale w przypadku bardziej złożonego kodu jest to znacznie lepsze niż używanie wywołań zwrotnych. Możesz łatwo łączyć wiele wywołań asynchronicznych za pomocą wielu theninstrukcji:

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

Możesz także łatwo owinąć odroczenia jQuery (które są zwracane z $.ajaxpołączeń):

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

Jak zauważył @charlietfl, jqXHRobiekt zwrócony przez $.ajax()implementuje Promiseinterfejs. Tak więc nie jest konieczne pakowanie go w Promise, można go użyć bezpośrednio:

$.ajax(...params...).then(function(result) {
    //whatever you want to do after the request
});
Domysee
źródło
Promise.resolveowijanie $.ajaxjest anty-wzorzec, ponieważ $.ajaxzwraca niewykonalną obietnicę
charlietfl
@charlietfl, ale to nie jest rzeczywiste Promise, po prostu implementuje interfejs. Nie jestem pewien, jak się zachowuje przy przekazywaniu wartości rzeczywistej Promisedo jego thenmetody, ani co Promise.allzrobiłby, gdyby a jqXHRi a Promisezostały do ​​niej przekazane. W tym celu muszę przeprowadzić dalsze testy. Ale dziękuję za podpowiedź, dodam ją do odpowiedzi!
Domysee
W rzeczywistości w jQuery wersja 3+ jest zgodna z Promises A +. Irregardless $.ajax.thennie jest nowy
charlietfl
21

Możesz też wywołać zdarzenie niestandardowe po zakończeniu jednej funkcji, a następnie powiązać ją z dokumentem:

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

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

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

Korzystając z tej metody, funkcja „b” może wykonać tylko PO funkcji „a”, ponieważ wyzwalacz istnieje tylko po zakończeniu wykonywania funkcji a.

de Raad
źródło
„Od jQuery 1.7 metoda .on () jest preferowaną metodą dołączania procedur obsługi zdarzeń do dokumentu.” api.jquery.com/bind
CodeVirtuoso,
5

możesz to zrobić w ten sposób

$.when(funtion1()).then(function(){
    funtion2();
})
Jay Momaya
źródło
js.do/code/465839 ok, działa ,,
Budi Mulyo
3

Zależy to od tego, co robi funkcja 1.

Jeśli funkcja1 wykonuje prosty, zsynchronizowany skrypt javascript, np. Aktualizuje wartość div lub coś takiego, funkcja 2 uruchomi się po zakończeniu funkcji 1.

Jeśli funkcja1 wykonuje wywołanie asynchroniczne, takie jak wywołanie AJAX, musisz utworzyć metodę „wywołania zwrotnego” (większość funkcji API ajax ma parametr funkcji wywołania zwrotnego). Następnie wywołaj funkcję 2 w wywołaniu zwrotnym. na przykład:

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

function MyCallback(result)
{
  function2(result);
}
Russell
źródło
2

Jeśli metoda 1 musi zostać wykonana po metodzie 2, 3, 4. Poniższy fragment kodu może być rozwiązaniem tego problemu za pomocą Odroczonego obiektu w 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
źródło
0

Jeśli funkcja1 jest funkcją synchronizacji, którą chcesz przekształcić w funkcję asynchroniczną, ponieważ jej ukończenie zajmuje trochę czasu i nie masz nad nią kontroli, aby dodać wywołanie zwrotne:

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

Dane wyjściowe będą:

Click handled

... i po 2 sekundach:

This is function 1
This is function 2

Działa to, ponieważ wywołanie metody window.setTimeout () doda zadanie do pętli zadań środowiska wykonawczego JS, co wykonuje wywołanie asynchroniczne, oraz ponieważ podstawowa zasada „uruchamiania do zakończenia” środowiska wykonawczego JS zapewnia, że ​​onClick () nigdy nie jest przerywany przed końcem.

Zauważ, że jest to tak zabawne, jak trudny do zrozumienia kod ...

StashOfCode
źródło