Neraka Panggil Balik
Untuk pengaturcara JavaScript, mengendalikan panggilan balik adalah perkara biasa, tetapi menangani panggilan balik yang terlalu dalam adalah tidak begitu indah. Contoh coretan kod di bawah menggunakan tiga lapisan panggilan balik ialah neraka panggilan balik yang legenda.
getDirectories(function(dirs) { getFiles(dirs[0], function(files) { getContent(files[0], function(file, content) { console.log('filename:', file); console.log(content); }); }); }); function getDirectories(callback) { setTimeout(function() { callback(['/home/ben']); }, 1000); } function getFiles(dir, callback) { setTimeout(function() { callback([dir + '/test1.txt', dir + '/test2.txt']); }, 1000) } function getContent(file, callback) { setTimeout(function() { callback(file, 'content'); }, 1000) }
Penyelesaian
Terdapat banyak penyelesaian tak segerak dalam ekosistem yang boleh menangani masalah neraka panggil balik, seperti bluebird, Q, dll. Artikel ini memfokuskan pada sokongan untuk pengaturcaraan tak segerak dalam spesifikasi ECMAScript 6/7.
Janji ES6
Promise ialah penyelesaian kepada pengaturcaraan tak segerak dan alat yang berkuasa untuk menyelesaikan masalah neraka panggil balik.
Promise telah diterima oleh arus perdana dalam ekosistem JavaScript pada tahun 2007 apabila rangka kerja Dojo menambahkan fungsi dojo.Deferred. Dengan populariti dojo.Deferred, pada tahun 2009 Kris Zyp mencadangkan spesifikasi CommonJS Promises/A. Selepas itu, sejumlah besar pelaksanaan Promise muncul dalam ekosistem, termasuk Q.js, FuturesJS, dsb. Sudah tentu, populariti Promise sebahagian besarnya disebabkan oleh kewujudan jQuery, tetapi jQuery tidak mematuhi sepenuhnya spesifikasi CommonJS Promises/A. Kemudian seperti yang anda lihat, spesifikasi ES 6 termasuk Janji.
MDN menerangkan Janji seperti ini:
Objek Promise ialah proksi yang mengembalikan nilai Nilai pulangan ini mungkin tidak diketahui apabila objek janji dibuat. Ia membolehkan anda menentukan kaedah pengendalian untuk kejayaan atau kegagalan operasi tak segerak. Ini membolehkan kaedah tak segerak mengembalikan nilai sama seperti kaedah segerak: kaedah tak segerak akan mengembalikan
yang mengandungi nilai pulangan asal.
Kod berikut ialah contoh dalam bahagian "Callback Hell" yang dilaksanakan melalui Promise Kod ini nampaknya tidak begitu ringkas, tetapi ia dipertingkatkan dengan ketara berbanding dengan panggilan balik hierarki tradisional, dan kod itu lebih mudah diselenggara dan dibaca.
getDirectories().then(function(dirs) { return getFiles(dirs[0]); }).then(function(files) { return getContent(files[0]); }).then(function(val) { console.log('filename:', val.file); console.log(val.content); }); function getDirectories() { return new Promise(function (resolve, reject) { setTimeout(function() { resolve(['/home/ben']); }, 1000); }); } function getFiles(dir) { return new Promise(function (resolve, reject) { setTimeout(function() { resolve([dir + '/test1.txt', dir + '/test2.txt']); }, 1000); }); } function getContent(file) { return new Promise(function (resolve, reject) { setTimeout(function() { resolve({file: file, content: 'content'}); }, 1000); }); }
Penjana ES6
Pelaksanaan Janji tidak cukup ringkas Kami juga memerlukan pilihan yang lebih baik, dan bersama adalah salah satu pilihan. co ialah pengawal aliran tak segerak berdasarkan Generator Sebelum memahami co, anda perlu memahami Generator terlebih dahulu. Pelajar yang biasa dengan C# harus tahu bahawa C# versi 2.0 memperkenalkan kata kunci hasil untuk penjana lelaran. ES 6 Generator adalah serupa dengan C# Ia juga menggunakan gula sintaks hasil dan melaksanakan mesin keadaan secara dalaman. Untuk penggunaan khusus, sila rujuk bahagian fungsi* dokumentasi MDN Untuk prinsip, sila rujuk blog pasukan AlloyTeam untuk pemahaman yang mendalam tentang Generator. Gunakan co untuk menggabungkan Penjana ES6 dan ES6 Promise dengan bijak untuk menjadikan panggilan tak segerak lebih harmoni.
co(function* (){ var dirs = yield getDirectories(); var files = yield getFiles(dirs[0]); var contentVal = yield getContent(files[0]); console.log('filename:', contentVal.file); console.log(contentVal.content); });
co adalah sangat bijak Kod terasnya boleh dipermudahkan kepada contoh berikut.
runGenerator(); function* run(){ var dirs = yield getDirectories(); var files = yield getFiles(dirs[0]); var contentVal = yield getContent(files[0]); console.log('filename:', contentVal.file); console.log(contentVal.content); } function runGenerator(){ var gen = run(); function go(result){ if(result.done) return; result.value.then(function(r){ go(gen.next(r)); }); } go(gen.next()); }
ES7 Async/Tunggu
ES6 Generator memang sangat bagus, tetapi malangnya ia memerlukan sokongan perpustakaan pihak ketiga. Berita baiknya ialah ES 7 akan memperkenalkan kata kunci Async/Await untuk menyelesaikan masalah panggilan tak segerak dengan sempurna. Nah, .net selangkah ke hadapan, rangka kerja .net 4.5 telah mendahului dalam menyokongnya.
Kod yang akan ditulis pada masa hadapan akan kelihatan seperti ini:
run(); async function run() { var dirs = await getDirectories(); var files = await getFiles(dirs[0]); var contentVal = await getContent(files[0]); console.log('filename:', contentVal.file); console.log(contentVal.content); }
Kesimpulan
Daripada kaedah pengaturcaraan tak segerak panggil balik klasik, kepada penambahbaikan pengaturcaraan tak segerak dalam spesifikasi ES6 Promise, kepada pemprosesan elegan bersama digabungkan dengan ES Generator, dan akhirnya pengakhiran sempurna ES7 async/wait, yang membolehkan kita memahami mengapa ciri ini muncul dalam ECMAScript Dan apakah masalah yang telah diselesaikan, dan kita dapat melihat dengan lebih jelas trend pembangunan pengaturcaraan tak segerak JavaScript.