1) Apa yang dimaksud dengan "callback hell" untuk seseorang yang tidak tahu javascript dan node.js?
Pertanyaan lain ini memiliki beberapa contoh neraka panggilan balik Javascript: Bagaimana menghindari bersarang lama fungsi asinkron di Node.js
Masalah dalam Javascript adalah bahwa satu-satunya cara untuk "membekukan" komputasi dan "sisanya" mengeksekusi yang terakhir (secara asinkron) adalah dengan meletakkan "sisanya" di dalam callback.
Misalnya, saya ingin menjalankan kode yang terlihat seperti ini:
x = getData();
y = getMoreData(x);
z = getMoreData(y);
...
Apa yang terjadi jika sekarang saya ingin membuat fungsi getData asynchronous, yang berarti saya mendapat kesempatan untuk menjalankan beberapa kode lain sementara saya menunggu mereka mengembalikan nilainya? Dalam Javascript, satu-satunya cara adalah dengan menulis ulang semua yang menyentuh komputasi asinkron menggunakan gaya penerusan lanjutan :
getData(function(x){
getMoreData(x, function(y){
getMoreData(y, function(z){
...
});
});
});
Saya rasa saya tidak perlu meyakinkan siapa pun bahwa versi ini lebih jelek dari versi sebelumnya. :-)
2) Kapan (dalam jenis pengaturan apa) "masalah callback hell" terjadi?
Bila Anda memiliki banyak fungsi panggilan balik dalam kode Anda! Semakin sulit untuk bekerja dengan mereka semakin banyak mereka yang Anda miliki di kode Anda dan itu menjadi sangat buruk ketika Anda perlu melakukan loop, blok coba-tangkap dan hal-hal seperti itu.
Misalnya, sejauh yang saya tahu, di JavaScript, satu-satunya cara untuk menjalankan serangkaian fungsi asinkron di mana seseorang dijalankan setelah pengembalian sebelumnya adalah menggunakan fungsi rekursif. Anda tidak dapat menggunakan for loop.
// we would like to write the following
for(var i=0; i<10; i++){
doSomething(i);
}
blah();
Sebaliknya, kita mungkin harus menulis:
function loop(i, onDone){
if(i >= 10){
onDone()
}else{
doSomething(i, function(){
loop(i+1, onDone);
});
}
}
loop(0, function(){
blah();
});
//ugh!
Jumlah pertanyaan yang kami dapatkan di StackOverflow yang menanyakan bagaimana melakukan hal semacam ini adalah bukti betapa membingungkannya :)
3) Mengapa itu terjadi?
Ini terjadi karena di JavaScript, satu-satunya cara untuk menunda komputasi agar berjalan setelah panggilan asinkron kembali adalah dengan meletakkan kode yang tertunda di dalam fungsi callback. Anda tidak dapat menunda kode yang ditulis dalam gaya sinkron tradisional sehingga Anda akan mendapatkan callback bersarang di mana-mana.
4) Atau dapatkah "callback hell" terjadi juga dalam satu aplikasi ulir?
Pemrograman asinkron berkaitan dengan konkurensi sedangkan utas tunggal berkaitan dengan paralelisme. Kedua konsep tersebut sebenarnya bukan hal yang sama.
Anda masih dapat memiliki kode serentak dalam satu konteks berulir. Faktanya, JavaScript, ratu neraka panggilan balik, adalah utas tunggal.
Apa perbedaan antara Concurrency dan Paralelisme?
5) dapatkah Anda juga menunjukkan bagaimana RX memecahkan "masalah callback hell" pada contoh sederhana itu.
Saya tidak tahu apa-apa tentang RX secara khusus, tetapi biasanya masalah ini diselesaikan dengan menambahkan dukungan asli untuk komputasi asinkron dalam bahasa pemrograman. Implementasinya dapat bervariasi dan termasuk: async, generator, coroutines, dan callcc.
Dengan Python kita dapat mengimplementasikan contoh loop sebelumnya dengan sesuatu di sepanjang baris:
def myLoop():
for i in range(10):
doSomething(i)
yield
myGen = myLoop()
Ini bukan kode lengkap tetapi idenya adalah bahwa "hasil" menghentikan perulangan for kami sampai seseorang memanggil myGen.next (). Yang penting adalah kita masih bisa menulis kode menggunakan for loop, tanpa perlu mengeluarkan logika "inside out" seperti yang harus kita lakukan dalam loop
fungsi rekursif tersebut .
Jawab saja pertanyaannya: bisakah Anda juga menunjukkan bagaimana RX memecahkan "masalah callback hell" pada contoh sederhana itu?
Keajaiban itu
flatMap
. Kita dapat menulis kode berikut di Rx untuk contoh @ hugomg:Ini seperti Anda menulis beberapa kode FP sinkronis, tetapi sebenarnya Anda dapat membuatnya asinkron dengan
Scheduler
.sumber
Untuk menjawab pertanyaan tentang bagaimana Rx memecahkan neraka panggilan balik :
Pertama mari kita gambarkan callback hell lagi.
Bayangkan sebuah kasus di mana kita harus melakukan http untuk mendapatkan tiga sumber daya - orang, planet, dan galaksi. Tujuan kita adalah menemukan galaksi tempat orang itu tinggal. Pertama kita harus menemukan orangnya, lalu planetnya, lalu galaksi. Itu tiga callback untuk tiga operasi asinkron.
Setiap callback bertingkat. Setiap callback batin bergantung pada induknya. Hal ini mengarah pada gaya "piramida malapetaka" dari panggilan balik neraka . Kode tersebut tampak seperti tanda>.
Untuk mengatasi ini di RxJs Anda dapat melakukan sesuatu seperti ini:
Dengan operator
mergeMap
AKA,flatMap
Anda bisa membuatnya lebih ringkas:Seperti yang Anda lihat, kode diratakan dan berisi satu rangkaian panggilan metode. Kami tidak memiliki "piramida kehancuran".
Oleh karena itu, neraka panggilan balik dihindari.
Jika Anda bertanya-tanya, promise adalah cara lain untuk menghindari callback hell, tetapi promise sangat menarik , tidak malas seperti yang dapat diamati dan (secara umum) Anda tidak dapat membatalkannya dengan mudah.
sumber
Callback hell adalah kode di mana penggunaan fungsi callback dalam kode async menjadi tidak jelas atau sulit diikuti. Umumnya, jika ada lebih dari satu level tipuan, kode yang menggunakan callback bisa menjadi lebih sulit untuk diikuti, lebih sulit untuk difaktor ulang, dan lebih sulit untuk diuji. Bau kode adalah beberapa tingkat lekukan karena melewatkan beberapa lapisan literal fungsi.
Hal ini sering terjadi ketika perilaku memiliki ketergantungan, yaitu ketika A harus terjadi sebelum B harus terjadi sebelum C. Kemudian Anda mendapatkan kode seperti ini:
Jika Anda memiliki banyak dependensi perilaku dalam kode Anda seperti ini, itu bisa menjadi masalah dengan cepat. Apalagi jika bercabang ...
Ini tidak akan berhasil. Bagaimana kita bisa membuat kode asynchronous dieksekusi dalam urutan yang ditentukan tanpa harus melewatkan semua callback ini?
RX adalah kependekan dari 'ekstensi reaktif'. Saya belum pernah menggunakannya, tetapi Googling menyarankan itu adalah kerangka kerja berbasis peristiwa, yang masuk akal. Peristiwa adalah pola umum untuk membuat kode dieksekusi secara berurutan tanpa membuat sambungan yang rapuh . Anda dapat membuat C mendengarkan acara 'bFinished' yang hanya terjadi setelah B dipanggil untuk mendengarkan 'aFinished'. Anda kemudian dapat dengan mudah menambahkan langkah tambahan atau memperluas perilaku semacam ini, dan dapat dengan mudah menguji apakah kode Anda dijalankan secara berurutan hanya dengan menyiarkan peristiwa dalam kasus pengujian Anda.
sumber
Panggil kembali neraka berarti Anda berada di dalam panggilan balik dari dalam panggilan balik lain dan itu pergi ke panggilan n sampai kebutuhan Anda tidak terpenuhi.
Mari kita pahami melalui contoh panggilan ajax palsu dengan menggunakan set timeout API, mari kita asumsikan kita memiliki API resep, kita perlu mengunduh semua resep.
Pada contoh di atas setelah 1,5 detik ketika timer berakhir di dalam kode panggilan balik akan dijalankan, dengan kata lain, melalui panggilan ajax palsu kita semua resep akan diunduh dari server. Sekarang kita perlu mendownload data resep tertentu.
Untuk mengunduh data resep tertentu, kami menulis kode di dalam panggilan balik pertama kami dan meneruskan ID resep.
Sekarang katakanlah kita perlu mengunduh semua resep dari penerbit resep yang sama dengan id 7638.
Untuk memenuhi kebutuhan kami yaitu mengunduh semua resep nama penerbit suru, kami menulis kode di dalam panggilan balik kedua kami. Jelas kami menulis rantai panggilan balik yang disebut neraka panggilan balik.
Jika Anda ingin menghindari callback hell, Anda bisa menggunakan Promise, yaitu fitur js es6, setiap Promise menerima panggilan balik yang dipanggil saat sebuah promise terisi penuh. janji panggilan balik memiliki dua opsi baik itu diselesaikan atau ditolak. Misalkan panggilan API Anda berhasil, Anda dapat memanggil penyelesaian dan meneruskan data melalui penyelesaian , Anda bisa mendapatkan data ini dengan menggunakan then () . Tetapi jika API Anda gagal, Anda dapat menggunakan reject, gunakan catch untuk menangkap kesalahan. Ingat janji selalu menggunakan kemudian untuk tekad dan menangkap untuk menolak
Mari kita selesaikan masalah callback hell sebelumnya menggunakan sebuah promise.
Sekarang unduh resep tertentu:
Sekarang kita dapat menulis metode lain memanggil allRecipeOfAPublisher seperti getRecipe yang juga akan mengembalikan sebuah janji, dan kita dapat menulis kemudian () lain untuk menerima janji penyelesaian untuk allRecipeOfAPublisher, saya harap pada titik ini Anda dapat melakukannya sendiri.
Jadi kita belajar bagaimana membuat dan menggunakan promise, sekarang mari kita buat penggunaan promise lebih mudah dengan menggunakan async / await yang diperkenalkan di es8.
Dalam contoh di atas, kami menggunakan fungsi async karena akan berjalan di latar belakang, di dalam fungsi async kami menggunakan kata kunci await sebelum setiap metode yang mengembalikan atau merupakan janji karena menunggu di posisi itu sampai janji itu terpenuhi, dengan kata lain di Kode di bawah sampai getIds selesai diselesaikan atau program tolak akan berhenti mengeksekusi kode di bawah baris itu ketika ID dikembalikan maka kita kembali memanggil fungsi getRecipe () dengan id dan menunggu dengan menggunakan kata kunci menunggu sampai data dikembalikan. Jadi inilah bagaimana akhirnya kami pulih dari neraka panggilan balik.
Untuk menggunakan await kita akan membutuhkan fungsi async, kita bisa mengembalikan sebuah promise jadi gunakan then untuk menyelesaikan promise dan cath untuk menolak promise
dari contoh di atas:
sumber
Salah satu cara untuk menghindari Callback adalah dengan menggunakan FRP yang merupakan "versi yang disempurnakan" dari RX.
Saya mulai menggunakan FRP baru-baru ini karena saya telah menemukan implementasi yang baik yang disebut
Sodium
( http://sodium.nz/ ).Kode tipikal terlihat seperti ini (Scala.js):
selectedNote.updates()
adalahStream
yang aktif jikaselectedNode
(yang merupakanCell
) berubah,NodeEditorWidget
kemudian diperbarui secara sesuai.Jadi, bergantung pada konten
selectedNode
Cell
, yang saat ini dieditNote
akan berubah.Kode ini menghindari Callback sepenuhnya, hampir, Cacllback-s didorong ke "lapisan luar" / "permukaan" aplikasi, tempat logika penanganan status berinteraksi dengan dunia luar. Tidak diperlukan Callback untuk menyebarkan data dalam logika penanganan status internal (yang mengimplementasikan mesin status).
Kode sumber lengkapnya adalah di sini
Cuplikan kode di atas sesuai dengan contoh Buat / Tampilkan / Perbarui sederhana berikut:
Kode ini juga mengirimkan pembaruan ke server, sehingga perubahan pada Entitas yang diperbarui disimpan ke server secara otomatis.
Semua penanganan acara ditangani dengan menggunakan
Stream
s danCell
s. Ini adalah konsep FRP. Callback hanya diperlukan jika logika FRP berinteraksi dengan dunia luar, seperti input pengguna, mengedit teks, menekan tombol, panggilan AJAX kembali.Aliran data dijelaskan secara eksplisit, secara deklaratif menggunakan FRP (diimplementasikan oleh pustaka Sodium), jadi tidak ada logika penanganan / panggilan balik peristiwa yang diperlukan untuk mendeskripsikan aliran data.
FRP (yang merupakan versi RX yang lebih "ketat") adalah cara untuk menggambarkan grafik aliran data, yang dapat berisi node yang berisi status. Peristiwa memicu perubahan status dalam status yang berisi node (disebut
Cell
s).Sodium adalah pustaka FRP orde tinggi, yang berarti bahwa menggunakan
flatMap
/switch
primitif dapat mengatur ulang grafik aliran data pada saat runtime.Saya merekomendasikan untuk melihat ke dalam buku Sodium , ini menjelaskan secara rinci bagaimana FRP menghilangkan semua Callback yang tidak penting untuk menggambarkan logika aliran data yang berkaitan dengan memperbarui status aplikasi sebagai respons terhadap beberapa rangsangan eksternal.
Menggunakan FRP, hanya Callback yang perlu disimpan yang menggambarkan interaksi dengan dunia luar. Dengan kata lain, aliran data dijelaskan secara fungsional / deklaratif ketika seseorang menggunakan kerangka FRP (seperti Sodium), atau ketika seseorang menggunakan kerangka "seperti FRP" (seperti RX).
Sodium juga tersedia untuk Javascript / Typecript.
sumber
Jika Anda tidak memiliki pengetahuan tentang callback dan hell callback tidak ada masalah, yang pertama adalah call back dan call back hell. Contoh: hell call back itu seperti kita bisa menyimpan kelas di dalam kelas. tentang itu bersarang dalam bahasa C, C ++. Nested Berarti sebuah kelas di dalam kelas lain.
sumber
Gunakan jazz.js https://github.com/Javanile/Jazz.js
itu menyederhanakan seperti ini:
sumber