Panggil suatu fungsi setelah fungsi sebelumnya selesai


179

Saya memiliki kode JavaScript berikut:

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

Bagaimana saya bisa memastikan yang function2dipanggil hanya setelah function1selesai?


24
adalah function1melakukan operasi async?
LiraNuna

8
Ya, jika function1 berisi animasi, function2 akan dieksekusi sementara animasi function1 masih terjadi. Bagaimana Anda membuat function2 menunggu sampai semuanya dimulai pada function1 selesai?
trusktr

Dapatkah seseorang menjelaskan kepada saya mengapa perlu ada sesuatu yang dilakukan untuk memastikan function2 tidak dipanggil sampai function1 selesai? Tidak ada operasi asinkron yang terjadi di sini sehingga function2 tidak akan berjalan sampai function1 selesai karena operasi ini sinkron.
Aaron

Jawaban:


206

Tentukan panggilan balik anonim, dan buat function1 menerimanya:

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


function function1(param, callback) {
  ...do stuff
  callback();
} 

11
+1 tetapi jika dia menggunakan 1,5 dia hanya dapat menggunakan pola Tangguhan baru
philwinkle

Terima kasih atas tanggapan cepatnya. Ya, function1 hanya melakukan tugas asinkron yang sederhana, jadi saya yakin Anda benar karena saya harus membuat panggilan balik - atau lihat bisnis yang ditangguhkan ini yang telah disebutkan oleh philwinkle. Terima kasih lagi, kawan. Saya akan tidur, dan menangani ini lagi di pagi hari.
Rrryyyaaannn

13
Jawaban ini bukan solusinya. Ini buktinya tidak berfungsi: jsfiddle.net/trusktr/M2h6e
trusktr

12
@ MikeRobinson Inilah masalahnya: Bagaimana jika ...do stuffberisi hal-hal yang tidak sinkron? Function2 masih tidak akan menyala saat diharapkan. Contoh dalam komentar saya di atas menunjukkan bahwa karena foo()fungsinya memiliki kode asinkron di dalamnya, bar()tidak memecat kontennya setelah foo()konten selesai dieksekusi, bahkan menggunakan $.when().then()untuk memanggil mereka satu demi satu. Jawaban ini hanya valid jika (dan hanya jika) semua hal di ...do stuffdalam kode Anda benar-benar sinkron.
trusktr

1
@trusktr Dalam hal ini Anda perlu menjaga melewati callback pertama sampai ke n th tingkat panggilan asynchronous, kemudian api callBack()setelah semua n asynchronous panggilan dilakukan. Karena OP belum menyebutkan apa yang dia lakukan dalam fungsinya, diasumsikan sebagai panggilan asinkron satu tingkat.
GoodSp33d

96

Jika Anda menggunakan jQuery 1.5 Anda dapat menggunakan pola Tangguhan baru:

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

Edit: Tautan blog yang diperbarui:

Rebecca Murphy memiliki artikel bagus tentang ini di sini: http://rmurphey.com/blog/2010/12/25/deferreds-coming-to-jquery/


2
Tetapi apakah itu contoh yang baik? Apa yang ditangguhkan? Anda menjalankan function1 dan function2 segera. (Saya bukan komentator asli.)
user113716

10
Itu sama sekali tidak bekerja untuk saya. function1 dan function2 keduanya dieksekusi pada waktu yang sama persis (seperti yang dapat diamati oleh mata manusia). Inilah contoh kecilnya: jsfiddle.net/trusktr/M2h6e
trusktr

1
Penulisan Rebecca Murphy dilakukan sebelum Defferds diperkenalkan ke jQuery dan menggunakan contoh-contoh berdasarkan bagaimana penulis berpikir itu akan diimplementasikan. Silakan kunjungi situs jQuery untuk melihat bagaimana sebenarnya menggunakan fitur ini: api.jquery.com/jQuery.Deferred
Spencer Williams

1
Bahkan dengan jQuery 1.5 atau sesuatu yang modern, tidakkah Anda mau $.when(function1).then(function2);(tanpa tanda kurung yang memanggil fungsi)?
Teepeemm

1
Membaca komentar ini terlambat beberapa tahun. function1harus mengembalikan janji. Dalam hal ini, itu haruslah fungsi yang dieksekusi, bukan referensi. Kapan resolvedipanggil janji itu maka function2dieksekusi. Ini mudah dijelaskan dengan mengasumsikan bahwa function1memiliki panggilan ajax. The donecallback kemudian akan menyelesaikan janji. Saya harap itu jelas.
philwinkle

46

Coba ini :

function method1(){
   // some code

}

function method2(){
   // some code
}

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

37

Jawaban ini menggunakan promises, fitur JavaScript ECMAScript 6standar. Jika platform target Anda tidak mendukung promises, isi dengan PromiseJs .

Janji adalah cara baru (dan jauh lebih baik) untuk menangani operasi asinkron dalam 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
    });
} 

Ini mungkin tampak seperti overhead yang signifikan untuk contoh sederhana ini, tetapi untuk kode yang lebih kompleks itu jauh lebih baik daripada menggunakan panggilan balik. Anda dapat dengan mudah membuat beberapa panggilan tidak sinkron menggunakan banyak thenpernyataan:

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

Anda juga dapat membungkus jQuery deferrds dengan mudah (yang dikembalikan dari $.ajaxpanggilan):

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

Seperti @charlietfl catat, jqXHRobjek dikembalikan dengan $.ajax()mengimplementasikan Promiseantarmuka. Jadi sebenarnya tidak perlu untuk membungkusnya Promise, itu dapat digunakan langsung:

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

Promise.resolvepembungkus $.ajaxadalah anti-pola sejak $.ajaxmengembalikan janji masa lalu
charlietfl

@charlietfl tetapi bukan yang sebenarnya Promise, itu hanya mengimplementasikan antarmuka. Saya tidak yakin bagaimana perilakunya ketika meneruskan nyata Promiseke thenmetodenya, atau apa yang Promise.allakan dilakukan jika a jqXHRdan a Promisediteruskan ke sana. Untuk itu saya perlu melakukan pengujian lebih lanjut. Tapi terima kasih atas petunjuknya, saya akan menambahkannya ke jawabannya!
Domysee

Sebenarnya di jQuery versi 3+ sesuai dengan Janji A +. Tidak peduli $.ajax.thenbukan hal baru
charlietfl

21

Atau Anda dapat memicu acara khusus ketika satu fungsi selesai, kemudian ikat ke dokumen:

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

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

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

Dengan menggunakan metode ini, fungsi 'b' hanya dapat menjalankan fungsi SETELAH 'a', karena pemicu hanya ada ketika fungsi a selesai dieksekusi.


"Pada jQuery 1.7, metode .on () adalah metode yang disukai untuk melampirkan penangan acara ke dokumen." api.jquery.com/bind
CodeVirtuoso


3

Ini tergantung pada apa yang dilakukan function1.

Jika function1 melakukan beberapa javascript synchrounous sederhana, seperti memperbarui nilai div atau sesuatu, maka function2 akan diaktifkan setelah function1 selesai.

Jika function1 melakukan panggilan asinkron, seperti panggilan AJAX, Anda harus membuat metode "panggilan balik" (sebagian besar API ajax memiliki parameter fungsi panggilan balik). Kemudian panggil function2 di dalam callback. misalnya:

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

function MyCallback(result)
{
  function2(result);
}

2

Jika metode 1 harus dijalankan setelah metode 2, 3, 4. Cuplikan kode berikut dapat menjadi solusi untuk ini menggunakan objek Ditangguhkan dalam 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>


0

Jika function1 adalah beberapa fungsi sinkronisasi yang ingin Anda ubah menjadi async karena butuh beberapa waktu untuk menyelesaikannya, dan Anda tidak memiliki kontrol atasnya untuk menambahkan panggilan balik:

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

Outputnya adalah:

Click handled

... dan setelah 2 detik:

This is function 1
This is function 2

Ini berfungsi karena memanggil window.setTimeout () akan menambahkan tugas ke loop tugas JS runtine, yang dibuat oleh panggilan async, dan karena prinsip dasar "run-to-completion" dari runtime JS memastikan bahwa onClick () tidak pernah terputus sebelum berakhir.

Perhatikan bahwa ini lucu karena mungkin kode ini sulit dimengerti ...

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.