Tetapkan nilai dari fungsi callback javascript

Jika Anda pernah mengalami masalah JavaScript ini, Anda tidak sendirian - masalah ini membuat banyak orang tersandung dan pada awalnya bisa rumit, untuk memahami dengan tepat cara memperbaikinya. Mari pertimbangkan contoh ini

1var array = [ ... ]; // An array with some objects 2for( var i = 0; i < array.length; ++i ) 3{ 4 $.doSthWithCallbacks( function() { 5 array[i].something = 42; 6 }); 7}

javascript

Meskipun kode ini terlihat baik-baik saja, ini menunjukkan kesalahpahaman dari konsep JavaScript yang sangat mendasar. Sekarang, jika Anda berpengalaman dalam Javascript, kesalahan ini seharusnya cukup mudah dikenali. Tetapi bagi kebanyakan orang, ini bukan masalahnya - beberapa orang benar-benar dapat menghabiskan waktu berjam-jam untuk mencari tahu mengapa kode mereka tidak berfungsi

Penjelasan

Ingat tutorial JavaScript pertama yang Anda baca, yang mengatakan bahwa JavaScript tidak sinkron? . Hal ini biasanya terjadi saat menggunakan API internal yang bergantung pada peristiwa eksternal. Misalnya, memproses respons setelah permintaan HTTP selesai atau setelah beberapa pemrosesan lainnya selesai

Jadi yang terjadi kemudian, adalah doSthWithCallbacks (ekspresi umum untuk semua fungsi JavaScript yang menggunakan panggilan balik) menjadwalkan fungsi panggilan balik menjadi . Tapi for_ loop tidak hanya menjadwalkan satu callback. Ini menjadwalkan panggilan balik senilai array.length dan pasti tidak akan diselesaikan dalam waktu yang sama for loop iteration. Each of those callbacks will be executed at an unpredictable time later on, when multiple for iterasi telah dilakukan, nilai if 1var array = [ ... ]; // An array with some objects 2 3function callbackClosure(i, callback) { 4 return function() { 5 return callback(i); 6 } 7} 8 9for( var i = 0; i < array.length; ++i ) 10{ 11 API.doSthWithCallbacks( callbackClosure( i, function(i) { 12 array[i].something = 42; 13 }) ); 14}1 is different and multiple other callbacks have also been scheduled.

Biasanya, callback tidak dieksekusi sampai for loop selesai, pada titik mana . Jadi, setiap kali salah satu callback dieksekusi, itu akan mengubah nilai terakhir dari array alih-alih nilai iterasi loop is exactly equal to 1var array = [ ... ]; // An array with some objects 2 3function callbackClosure(i, callback) { 4 return function() { 5 return callback(i); 6 } 7} 8 9for( var i = 0; i < array.length; ++i ) 10{ 11 API.doSthWithCallbacks( callbackClosure( i, function(i) { 12 array[i].something = 42; 13 }) ); 14}4. So, every time any of the callbacks is executed it will be modifying the last value of the array instead of the value of the for yang dijadwalkan . Tentu saja, seperti yang saya katakan, tidak dapat diprediksi kapan callback akan dieksekusi dan bergantung pada banyak faktor yang digunakan oleh juru bahasa JavaScript, fungsi yang memanggil callback dan data inputnya. Contohnya adalah permintaan HTTP dengan callback sukses yang tidak akan dieksekusi sebelum server mengirimkan respons, yang bisa berupa interval waktu antara beberapa milidetik dan beberapa menit.

Bagaimana menyiasatinya

Saya akan memberi Anda dua solusi tentang cara mengatasi masalah tersebut. Keduanya sangat efisien dalam hal kinerja dan konsumsi memori. Metode pertama lebih mudah dipahami, tetapi memerlukan definisi fungsi di lingkup atas, yang membuat kode agak sulit dibaca karena Anda harus mencari fungsinya. Solusi kedua adalah favorit pribadi saya tetapi lebih sulit untuk dipahami, terutama ketika Anda melihat konstruksi seperti itu untuk pertama kalinya. Ada solusi lain, tetapi saat ini adalah yang tercepat dan didukung di semua browser utama dan juru bahasa JavaScript

Pada dasarnya kedua metode melakukan tugas yang sama tetapi dengan cara yang berbeda. Apa yang mereka lakukan adalah membuat fungsi panggilan balik terpisah dengan salinan mereka sendiri dari nilai 1var array = [ ... ]; // An array with some objects 2 3function callbackClosure(i, callback) { 4 return function() { 5 return callback(i); 6 } 7} 8 9for( var i = 0; i < array.length; ++i ) 10{ 11 API.doSthWithCallbacks( callbackClosure( i, function(i) { 12 array[i].something = 42; 13 }) ); 14}1 dalam cakupan yang hanya tersedia bagi mereka.

Penutupan dalam suatu fungsi

Metode ini relatif mudah dipahami dan itulah sebabnya saya tidak akan membahasnya secara mendetail. Ini tidak terjadi dengan penutupan inline, jadi saya akan membahasnya secara mendalam. Fungsi 1var array = [ ... ]; // An array with some objects 2 3function callbackClosure(i, callback) { 4 return function() { 5 return callback(i); 6 } 7} 8 9for( var i = 0; i < array.length; ++i ) 10{ 11 API.doSthWithCallbacks( callbackClosure( i, function(i) { 12 array[i].something = 42; 13 }) ); 14}7 mengembalikan fungsi yang memanggil callback aktual dengan salinan eksplisit 1var array = [ ... ]; // An array with some objects 2 3function callbackClosure(i, callback) { 4 return function() { 5 return callback(i); 6 } 7} 8 9for( var i = 0; i < array.length; ++i ) 10{ 11 API.doSthWithCallbacks( callbackClosure( i, function(i) { 12 array[i].something = 42; 13 }) ); 14}1 as an argument.

1var array = [ ... ]; // An array with some objects 2 3function callbackClosure(i, callback) { 4 return function() { 5 return callback(i); 6 } 7} 8 9for( var i = 0; i < array.length; ++i ) 10{ 11 API.doSthWithCallbacks( callbackClosure( i, function(i) { 12 array[i].something = 42; 13 }) ); 14}_

javascript

Karena setiap fungsi menyatakan ruang lingkupnya sendiri, dan 1var array = [ ... ]; // An array with some objects 2 3function callbackClosure(i, callback) { 4 return function() { 5 return callback(i); 6 } 7} 8 9for( var i = 0; i < array.length; ++i ) 10{ 11 API.doSthWithCallbacks( callbackClosure( i, function(i) { 12 array[i].something = 42; 13 }) ); 14}1 memiliki tipe atom dasar ( 1(function() { 2 // Something declared here will only be available to the function below. 3 // Code here is executed only once upon the creation of the inner function 4 return function(callbackArguments) { 5 // Actual callback here 6 }; 7})(); // The last brackets execute the outer function0< . ) it is not passed as a reference, but rather as a copy (unlike objects) which ensures that the actual callback will be executed against the correct value.

Penutupan sebaris

Ini membawa kita ke peretasan JavaScript favorit saya. Ini dilakukan dengan mendeklarasikan fungsi anonim yang disebut sendiri, yang umumnya terlihat seperti ini

1(function() { 2 // Something declared here will only be available to the function below. 3 // Code here is executed only once upon the creation of the inner function 4 return function(callbackArguments) { 5 // Actual callback here 6 }; 7})(); // The last brackets execute the outer function_

javascript

Perhatikan bahwa fungsi luar hanya digunakan untuk mengenkapsulasi fungsi dalam, dan membuat lingkup variabel terpisah untuk fungsi dalam. Selain itu, fungsi luar mengembalikan nilai bertipe 1(function() { 2 // Something declared here will only be available to the function below. 3 // Code here is executed only once upon the creation of the inner function 4 return function(callbackArguments) { 5 // Actual callback here 6 }; 7})(); // The last brackets execute the outer function1 yang merupakan tipe yang tepat untuk callback. Jadi, menerapkan ini pada contoh sebelumnya kita sampai di sini.

1var array = [ ... ]; // An array with some objects 2for( var i = 0; i < array.length; ++i ) 3{ 4 API.doSthWithCallbacks( (function() { 5 var j = i; // j is a copy of i only available to the scope of the inner function 6 return function() { 7 array[j].something = 42; 8 } 9 })() ); 10}

javascript

Jika, misalnya, Anda harus melakukan beberapa pemrosesan asinkron, dan harus ada beberapa kode agregat yang hanya boleh dijalankan setelah semua callback selesai, yang perlu Anda lakukan adalah mengetahui berapa banyak callback yang Anda miliki . Jika 1(function() { 2 // Something declared here will only be available to the function below. 3 // Code here is executed only once upon the creation of the inner function 4 return function(callbackArguments) { 5 // Actual callback here 6 }; 7})(); // The last brackets execute the outer function2 sama dengan 1(function() { 2 // Something declared here will only be available to the function below. 3 // Code here is executed only once upon the creation of the inner function 4 return function(callbackArguments) { 5 // Actual callback here 6 }; 7})(); // The last brackets execute the outer function3 artinya Anda sedang memproses .

1var array = [ ... ]; // An array with some objects 2var count = 0, length = array.length; 3for( var i = 0; i < array.length; ++i ) 4{ 5 API.doSthWithCallbacks( (function() { 6 var j = i; // A copy of i only available to the scope of the inner function 7 return function() { 8 array[j].something = 42; 9 10 ++count; 11 if( count == length ) { 12 // Code executed only after all the processing tasks have been completed 13 } 14 } 15 })() ); 16}

javascript

Sekarang, pada titik ini mudah untuk menjadi bingung. Apakah ________9______4 operasi atomik? . Beberapa menganggap sesuatu seperti mutex atau semaphore. Tapi ini tidak benar.

Meskipun JavaScript asinkron, itu bukan multithreaded. Bahkan, meskipun tidak mungkin untuk memprediksi kapan panggilan balik akan dieksekusi, kondisi balapan dijamin tidak akan terjadi karena JavaScript hanya berjalan dalam satu utas. (Sebagai catatan tambahan, bukan berarti tidak ada cara untuk menjalankan banyak utas dalam JavaScript. Lihat API Pekerja Web untuk JavaScript jenis Web)

ES6

ECMAScript 6 memperkenalkan 1(function() { 2 // Something declared here will only be available to the function below. 3 // Code here is executed only once upon the creation of the inner function 4 return function(callbackArguments) { 5 // Actual callback here 6 }; 7})(); // The last brackets execute the outer function5 kata kunci yang memungkinkan Anda mendeklarasikan variabel yang dicakup ke blok terlampir terdekat dan bukan seperti global 1(function() { 2 // Something declared here will only be available to the function below. 3 // Code here is executed only once upon the creation of the inner function 4 return function(callbackArguments) { 5 // Actual callback here 6 }; 7})(); // The last brackets execute the outer function6 does. Thus the closure problem can be solved simply by replacing 1(function() { 2 // Something declared here will only be available to the function below. 3 // Code here is executed only once upon the creation of the inner function 4 return function(callbackArguments) { 5 // Actual callback here 6 }; 7})(); // The last brackets execute the outer function6 dengan 1(function() { 2 // Something declared here will only be available to the function below. 3 // Code here is executed only once upon the creation of the inner function 4 return function(callbackArguments) { 5 // Actual callback here 6 }; 7})(); // The last brackets execute the outer function5 .

1var array = [ ... ]; // An array with some objects 2for( let i = 0; i < array.length; ++i ) 3{ 4 $.doSthWithCallbacks( function() { 5 array[i].something = 42; 6 }); 7}

js

Rapi, bukan?

Soal latihan

Saya telah menyiapkan Soal Latihan yang mendemonstrasikan masalahnya dan jika Anda mau, Anda dapat mencobanya. Saat mencobanya, hanya edit kode di dalam bagian yang ditentukan. Ada banyak cara untuk melakukannya, tetapi salah satu yang terbaik adalah menggunakan teknik kedua yang saya tunjukkan

Tentang Penulis

Itay Grudev adalah mahasiswa yang sedang mengejar gelar di bidang Ilmu Komputer dan Fisika di University of Aberdeen, Inggris

Bisakah saya mengembalikan nilai dari JavaScript fungsi panggilan balik?

Fungsi panggilan balik dapat mengembalikan nilai , dengan kata lain, tetapi kode yang memanggil fungsi tidak akan memperhatikan nilai yang dikembalikan. Ya, masuk akal untuk mencoba dan mengembalikan nilai dari panggilan balik janji.

Bagaimana Anda mengakses variabel dalam fungsi panggilan balik?

3 metode untuk mengakses this yang benar di dalam callback .
Gunakan fungsi panah. Fungsi panah JavaScript diperkenalkan di ECMAScript 6. .
Buat variabel lain untuk menyimpan objek ini. .
Ikat ini secara eksplisit ke objek

Bagaimana cara melewatkan fungsi panggilan balik dalam JavaScript?

Dalam JavaScript, cara membuat fungsi callback adalah dengan meneruskannya sebagai parameter ke fungsi lain, lalu memanggilnya kembali tepat setelah sesuatu terjadi atau beberapa tugas selesai . .

Bagaimana cara menjalankan satu fungsi demi satu dalam JavaScript?

kapan(). then() untuk memanggil mereka satu per satu.

Postingan terbaru

LIHAT SEMUA