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?

Rrryyyaaannn
sumber
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();
} 
Mike Robinson
sumber
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/

Philwinkle
sumber
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();
}
})
TuanTruong
sumber
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
});
Domysee
sumber
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.

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

Anda bisa melakukannya seperti ini

$.when(funtion1()).then(function(){
    funtion2();
})
Jay Momaya
sumber
js.do/code/465839 ok, berfungsi ,,
Budi Mulyo
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);
}
Russell
sumber
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>

Sahadeb Patro
sumber
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 ...

StashOfCode
sumber