Jawaban Fabrício sangat tepat; tetapi saya ingin melengkapi jawabannya dengan sesuatu yang kurang teknis, yang berfokus pada analogi untuk membantu menjelaskan konsep asinkronisitas .
Analogi ...
Kemarin, pekerjaan yang saya lakukan memerlukan beberapa informasi dari seorang kolega. Saya meneleponnya; Beginilah percakapan berlangsung:
Me : Hi Bob, saya perlu tahu bagaimana kita foo 'd yang bar ' d pekan lalu. Jim ingin laporan tentang itu, dan kaulah satu-satunya yang tahu detail tentang itu.
Bob : Tentu saja, tetapi itu akan memakan waktu sekitar 30 menit?
Saya : Bob luar biasa. Berikan saya cincin kembali ketika Anda mendapat informasi!
Pada titik ini, saya menutup telepon. Karena saya memerlukan informasi dari Bob untuk menyelesaikan laporan saya, saya meninggalkan laporan dan pergi untuk minum kopi, kemudian saya menyusul beberapa email. 40 menit kemudian (Bob lambat), Bob menelepon kembali dan memberi saya informasi yang saya butuhkan. Pada titik ini, saya melanjutkan pekerjaan saya dengan laporan saya, karena saya memiliki semua informasi yang saya butuhkan.
Bayangkan jika percakapan sudah seperti ini sebagai gantinya;
Me : Hi Bob, saya perlu tahu bagaimana kita foo 'd yang bar ' d pekan lalu. Jim ingin melaporkannya, dan kaulah satu-satunya yang tahu detailnya.
Bob : Tentu saja, tetapi itu akan memakan waktu sekitar 30 menit?
Saya : Bob hebat. Aku akan menunggu.
Dan saya duduk di sana dan menunggu. Dan menunggu. Dan menunggu. Selama 40 menit. Tidak melakukan apa-apa selain menunggu. Akhirnya, Bob memberi saya informasi, kami menutup telepon, dan saya menyelesaikan laporan saya. Tapi saya kehilangan 40 menit produktivitas.
Ini asinkron vs perilaku sinkron
Inilah yang terjadi dalam semua contoh dalam pertanyaan kami. Memuat gambar, memuat file dari disk, dan meminta halaman melalui AJAX semuanya berjalan lambat (dalam konteks komputasi modern).
Daripada menunggu operasi lambat ini selesai, JavaScript memungkinkan Anda mendaftarkan fungsi panggilan balik yang akan dieksekusi ketika operasi lambat selesai. Sementara itu, JavaScript akan terus mengeksekusi kode lain. Fakta bahwa JavaScript mengeksekusi kode lain sambil menunggu operasi yang lambat untuk menyelesaikan membuat perilaku tidak sinkron . Seandainya JavaScript menunggu operasi untuk diselesaikan sebelum mengeksekusi kode lain, ini akan menjadi perilaku sinkron .
var outerScopeVar;
var img = document.createElement('img');
// Here we register the callback function.
img.onload = function() {
// Code within this function will be executed once the image has loaded.
outerScopeVar = this.width;
};
// But, while the image is loading, JavaScript continues executing, and
// processes the following lines of JavaScript.
img.src = 'lolcat.png';
alert(outerScopeVar);
Dalam kode di atas, kami meminta JavaScript untuk memuat lolcat.png
, yang merupakan operasi sloooow . Fungsi panggilan balik akan dieksekusi setelah operasi lambat ini selesai, tetapi sementara itu, JavaScript akan terus memproses baris kode berikutnya; yaitu alert(outerScopeVar)
.
Inilah sebabnya kami melihat lansiran ditampilkan undefined
; sejak alert()
diproses segera, bukan setelah gambar telah dimuat.
Untuk memperbaiki kode kita, yang harus kita lakukan adalah memindahkan alert(outerScopeVar)
kode ke fungsi panggilan balik. Sebagai konsekuensi dari ini, kita tidak perlu lagi outerScopeVar
variabel dideklarasikan sebagai variabel global.
var img = document.createElement('img');
img.onload = function() {
var localScopeVar = this.width;
alert(localScopeVar);
};
img.src = 'lolcat.png';
Anda akan selalu melihat panggilan balik ditentukan sebagai fungsi, karena itulah satu-satunya * cara dalam JavaScript untuk mendefinisikan beberapa kode, tetapi tidak menjalankannya sampai nanti.
Karena itu, dalam semua contoh kami, function() { /* Do something */ }
ini adalah callback; untuk memperbaiki semua contoh, yang harus kita lakukan adalah memindahkan kode yang memerlukan respons operasi ke sana!
* Secara teknis Anda dapat menggunakan eval()
juga, tetapi eval()
jahat untuk tujuan ini
Bagaimana cara membuat penelepon saya menunggu?
Saat ini Anda mungkin memiliki beberapa kode yang mirip dengan ini;
function getWidthOfImage(src) {
var outerScopeVar;
var img = document.createElement('img');
img.onload = function() {
outerScopeVar = this.width;
};
img.src = src;
return outerScopeVar;
}
var width = getWidthOfImage('lolcat.png');
alert(width);
Namun, kita sekarang tahu bahwa itu return outerScopeVar
terjadi segera; sebelum onload
fungsi callback memperbarui variabel. Ini mengarah ke getWidthOfImage()
pengembalian undefined
, dan undefined
diperingatkan.
Untuk memperbaikinya, kita perlu mengizinkan fungsi panggilan getWidthOfImage()
untuk mendaftarkan panggilan balik, lalu memindahkan peringatan lebar menjadi dalam panggilan balik itu;
function getWidthOfImage(src, cb) {
var img = document.createElement('img');
img.onload = function() {
cb(this.width);
};
img.src = src;
}
getWidthOfImage('lolcat.png', function (width) {
alert(width);
});
... seperti sebelumnya, perhatikan bahwa kami dapat menghapus variabel global (dalam hal ini width
).