Artikel ini terutama mempopulerkan penggunaan janji.
Untuk waktu yang lama, JavaScript selalu menangani secara tidak sinkron dalam bentuk panggilan balik, dan mekanisme panggilan balik di bidang pengembangan front-end hampir berakar dalam di hati orang-orang. Saat merancang API, apakah itu produsen browser, pengembang SDK, atau penulis berbagai perpustakaan, mereka pada dasarnya mengikuti rutinitas panggilan balik.
Dalam beberapa tahun terakhir, dengan kematangan bertahap dari model pengembangan JavaScript, spesifikasi CommonJS telah lahir, termasuk proposal spesifikasi janji. Promise telah sepenuhnya mengubah penulisan pemrograman JS asinkron, membuat pemrograman asinkron sangat mudah dimengerti.
Dalam model callback, kami berasumsi bahwa antrian asinkron perlu dieksekusi, dan kode mungkin terlihat seperti ini:
loadImg ('a.jpg', function () {loadImg ('b.jpg', function () {loadImg ('c.jpg', function () {console.log ('semua selesai!');});});});Inilah yang sering kita sebut piramida balik. Ketika ada banyak tugas asinkron, mempertahankan sejumlah besar panggilan balik akan menjadi bencana. Saat ini, Node.js sangat populer. Tampaknya banyak tim ingin menggunakannya untuk membuat sesuatu untuk bergaul dengan "mode". Setelah mengobrol dengan teman sekelas operasi dan pemeliharaan, mereka juga berencana menggunakan Node.js untuk melakukan sesuatu, tetapi ketika mereka memikirkan lapisan callback JS, mereka tidak disarankan.
Oke, omong kosong sudah berakhir, mari kita mulai topik.
Janji mungkin terbiasa dengan semua orang, karena spesifikasi janji telah keluar untuk waktu yang lama, dan janji telah dimasukkan dalam ES6, dan versi yang lebih tinggi dari browser Chrome dan Firefox telah menerapkan janji secara asli, tetapi ada lebih sedikit API daripada perpustakaan kelas janji populer saat ini.
Janji yang disebut dapat dipahami secara harfiah sebagai "janji", yang berarti bahwa panggilan B, B mengembalikan A "janji" ke A, dan kemudian A dapat menulis ini ketika menulis rencana: ketika B mengembalikan hasilnya kepada saya, A mengeksekusi rencana S1. Sebaliknya, jika B tidak memberikan hasil yang diinginkan untuk beberapa alasan, maka A mengeksekusi rencana darurat S2, sehingga semua risiko potensial berada dalam kisaran A yang dapat dikendalikan.
Kalimat di atas diterjemahkan ke dalam kode yang mirip dengan:
var resb = b (); var runa = fungsi () {resb.then (execs1, execs2);}; runa ();Hanya melihat baris kode di atas, tampaknya tidak ada yang istimewa. Tetapi kenyataannya mungkin jauh lebih rumit dari ini. Untuk mencapai satu hal, A May mengandalkan respons lebih dari satu orang B. Mungkin perlu meminta banyak orang pada waktu yang sama dan kemudian menerapkan rencana berikutnya setelah menerima semua jawaban. Terjemahan terakhir ke dalam kode mungkin terlihat seperti ini:
var resb = b (); var rescor = c (); ... var runa = fungsi () {reqb .then (rescs, execs2) .then (resd, execs3) .then (rese, execs4) ... .then (execs1);}; runa ();Di sini, mekanisme pemrosesan yang berbeda digunakan ketika masing -masing yang ditanyakan merespons yang tidak sejalan dengan harapan. Faktanya, spesifikasi janji tidak memerlukan ini, dan Anda bahkan tidak dapat melakukan apa pun (mis., Tidak lulus dalam parameter kedua saat itu) atau menanganinya secara seragam.
Oke, mari kita ketahui spesifikasi janji/a+:
then (dapat dikatakan bahwa kemudian adalah inti dari janji), dan kemudian harus mengembalikan janji. Kemudian dari janji yang sama dapat dipanggil beberapa kali, dan urutan eksekusi callback konsisten dengan urutan ketika mereka didefinisikanSeperti yang Anda lihat, tidak ada banyak konten dalam spesifikasi janji, sehingga Anda dapat mencoba menerapkan janji berikut sendiri.
Berikut ini adalah implementasi sederhana dari janji bahwa saya telah merujuk banyak perpustakaan janji. Harap pindah ke Promisea dalam kode.
Analisis singkat tentang ide -ide tersebut:
Janji konstruktor menerima resolver fungsi, yang dapat dipahami sebagai tugas yang tidak sinkron. Resolver menerima dua parameter, satu adalah panggilan balik ketika berhasil dan yang lainnya adalah panggilan balik saat gagal. Dua parameter ini sama dengan parameter yang dilewati.
Yang kedua adalah implementasi saat itu. Karena janji itu mensyaratkan bahwa kemudian harus mengembalikan janji, janji baru akan dihasilkan ketika kemudian dipanggil, yang akan digantung pada _next dari janji saat ini. Beberapa panggilan dari janji yang sama hanya akan mengembalikan _next yang dihasilkan sebelumnya.
Karena dua parameter yang diterima dengan metode itu adalah opsional dan tidak ada batasan pada jenis, itu bisa berupa fungsi, nilai tertentu, atau janji lain. Berikut adalah implementasi spesifik dari itu:
Janji.prototype.then = function (resolve, reject) {var next = this._next || (this._next = janji ()); status var = this.status; var x; if ('tertunda' === status) {isfn (resolve) && this._resolves.push (resolve); isfn (tolak) && this._rejects.push (tolak); kembali berikutnya; } if ('diselesaikan' === status) {if (! isfn (resolve)) {next.resolve (resolve); } else {coba {x = resolve (this.value); resolvex (selanjutnya, x); } catch (e) {this.reject (e); }} kembali selanjutnya; } if ('ditolak' === status) {if (! isfn (reject)) {next.rect (reject); } else {coba {x = tolak (this.reason); resolvex (selanjutnya, x); } catch (e) {this.reject (e); }} kembali selanjutnya; }};Di sini, kemudian telah menyederhanakan implementasi perpustakaan kelas janji lainnya, dan implementasinya jauh lebih kompleks daripada ini, dan juga memiliki lebih banyak fungsi. Misalnya, ada parameter ketiga - beri tahu, yang menunjukkan kemajuan janji saat ini, yang sangat berguna ketika mengunggah file desain, dll. Pemrosesan berbagai parameter saat itu adalah bagian yang paling rumit. Siswa yang tertarik dapat merujuk pada implementasi jenis perpustakaan janji lainnya.
Atas dasar saat itu, setidaknya dua metode harus diperlukan, yaitu, untuk menyelesaikan konversi keadaan janji dari yang tertunda untuk diselesaikan atau ditolak, dan untuk melaksanakan antrian callback yang sesuai, yaitu metode resolve() dan reject() .
Pada titik ini, janji sederhana telah dirancang. Berikut ini adalah implementasi sederhana dari dua fungsi yang dijanjikan berikut:
function sleep (ms) {return function (v) {var p = janji (); setTimeout (function () {p.resolve (v);}, ms); mengembalikan P; };}; fungsi getimg (url) {var p = janji (); var img = gambar baru (); img.onload = function () {p.resolve (this); }; img.onError = function (err) {P.REJECT (err); }; img.url = url; mengembalikan p;}; Karena konstruktor janji menerima tugas yang tidak sinkron sebagai parameter, getImg juga dapat disebut seperti ini:
fungsi getImg (url) {return janji (fungsi (resolve, reject) {var img = gambar baru (); img.onload = function () {resolve (this);}; img.onerror = function (err) {reject (err);}; img.url = url;});};Berikutnya (momen menyaksikan keajaiban), misalkan ada persyaratan BT untuk mengimplementasikan ini: dapatkan konfigurasi JSON secara tidak sinkron, parsing data JSON dan dapatkan gambar di dalam, dan kemudian memuat gambar secara berurutan, dan memberikan efek pemuatan ketika tidak ada gambar yang dimuat.
fungsi addimg (img) {$ ('#list'). find ('> li: last-child'). html (''). append (img);}; function prepend () {$ ('<li>') .html ('loading ...') .AppendTo ($ ('#list');};} (') (hide) (hide) (hide) (hide) (hide) (hide (hide) (hide) (hide () (hide) (hide (hide) (hide) (hide () (hide) (hide () (hide (hide) (hide) (hide (hide) (hide) (HIVER () (hide) (HIVER () (HIVER () (HIVER (). getData('map.json') .then(function(data) { $('h4').html(data.name); return data.list.reduce(function(promise, item) { return promise .then(prepend) .then(sleep(1000)) .then(function() { return getImg(item.url); }) .then(addImg); }, Janji.Resolve ());}) .then (sleep (300)) .then (function () {$ ('#done'). Show ();});}; $ ('#run'). On ('klik', run);Tidur di sini baru saja ditambahkan untuk melihat efeknya, Anda dapat mengklik untuk melihat demo! Tentu saja, contoh Node.js dapat dilihat di sini.
Di sini, metode statis Promise.resolve(v) hanya mengembalikan janji dengan V sebagai hasil positif. V tidak dapat diteruskan, atau dapat berupa fungsi atau objek atau fungsi yang mengandung then metode (mis. Maka).
Metode statis yang serupa termasuk Promise.cast(promise) , yang menghasilkan janji dengan janji sebagai hasil positif;
Promise.reject(reason) menghasilkan janji dengan alasan sebagai hasil negatif.
Skenario penggunaan kami yang sebenarnya mungkin sangat kompleks, dan seringkali membutuhkan beberapa tugas asinkron untuk dieksekusi secara diselingi, paralel atau serial. Pada saat ini, Anda dapat membuat berbagai ekstensi untuk menjanjikan, seperti menerapkan Promise.all() Promise.any() .
Anda dapat merujuk pada artikel ini dalam janji JavaScript HTML5Rocks. Saat ini, browser canggih seperti Chrome dan Firefox memiliki objek janji bawaan, memberikan lebih banyak antarmuka operasi, seperti Promise.all() , yang mendukung lewat dalam array janji, dan kemudian mengeksekusi ketika semua janji selesai. Ada juga penangkapan pengecualian yang lebih ramah dan kuat, yang seharusnya cukup untuk berurusan dengan pemrograman asinkron harian.
Perpustakaan JS yang paling populer saat ini telah menerapkan janji untuk berbagai tingkat, seperti dojo, jQuery, zepto, when.js, q, dll., Tetapi sebagian besar objek yang terpapar Deferred . Mengambil jQuery (Zepto serupa) sebagai contoh, terapkan getImg() :
fungsi getimg (url) {var def = $ .deferred (); var img = gambar baru (); img.onload = function () {def.resolve (this); }; img.onError = function (err) {def.Rect (err); }; img.src = url; return def.promise ();}; Tentu saja, di jQuery, banyak operasi yang kembali ditangguhkan atau janji, seperti animate dan ajax :
// Ajax $ .Ajax (Opacity ': 0}, 1000) .promise () .then (function () {console.log (' Done ');}); // Ajax $ .AJAX (opsi) .hen kemudian Sukses, Gagal); $. Ajax (opsi) .done (success). Fail (Failur $ .AJAX (options2)) .then (function () {console.log ('semua dilakukan.');}, function () {console.error ('ada sesuatu yang salah.');}); JQuery juga mengimplementasikan metode done() dan fail() , yang sebenarnya merupakan jalan pintas dari metode itu.
Untuk menangani antrian janji, jQuery mengimplementasikan metode $.when() , dan penggunaannya mirip dengan Promise.all() .
Untuk perpustakaan kelas lainnya, perlu disebutkan di sini bahwa ketika.js memiliki sedikit kode, itu sepenuhnya mengimplementasikan janji, mendukung browser dan node.js, dan menyediakan API yang lebih kaya, yang merupakan pilihan yang baik. Karena keterbatasan ruang, kami tidak akan mengembangkannya lagi.
Kami melihat bahwa tidak peduli seberapa rumit implementasi janji itu, penggunaannya sangat sederhana dan kode organisasi sangat jelas. Mulai sekarang, tidak perlu disiksa oleh panggilan balik.
Akhirnya, janji itu sangat elegan! Tapi janji hanya memecahkan masalah sarang panggilan balik yang dalam. Ini adalah generator yang benar -benar menyederhanakan pemrograman asinkron JavaScript. Di sisi Node.js, disarankan untuk mempertimbangkan generator.
Artikel berikutnya, Studi Generator.
Teks asli Github: https://github.com/chemdemo/chemdemo.github.io/issues/6