Kata pengantar
Pertama kali saya bersentuhan dengan Promise adalah ketika Microsoft merilis sistem operasi Windows 8 pada tahun 2012 dan belajar menggunakan HTML5 untuk menulis aplikasi metro dengan sikap yang aneh. Pada waktu itu, antarmuka asinkron di perpustakaan WINJS yang dilengkapi dengan HTML5 semuanya dalam bentuk janji, yang hanyalah sebuah buku surga bagi saya yang baru saja lulus dari Javascript pada waktu itu. Apa yang saya pikirkan saat itu adalah bahwa Microsoft mengutak -atiknya lagi.
Tanpa diduga, pada tahun 2015, Promise sebenarnya ditulis dalam standar ES6. Selain itu, survei menunjukkan bahwa pemrogram JS menggunakan hal ini cukup tinggi.
Ironisnya, seperti Microsoft, yang secara luas menggunakan janji dalam antarmuka pengembangan aplikasi metro pada awal 2012, browsernya sendiri IE masih tidak mendukung janji sampai mati pada tahun 2015. Tampaknya Microsoft tidak memiliki teknologi ini, tetapi benar -benar memberikan perawatan untuk IE. . .
Melihat ke belakang sekarang, hal yang paling merepotkan tentang melihat janji pada waktu itu adalah bahwa pemula tampak luar biasa dan juga merupakan fitur yang paling dipuji oleh programmer JS: kemudian rantai panggilan fungsi.
Kemudian rantai panggilan fungsi, pada dasarnya, adalah untuk memanggil beberapa proses asinkron secara berurutan. Artikel ini dimulai dari titik ini dan mempelajari dan mempelajari fitur janji.
Janji terpecahkan
Pertimbangkan skenario berikut, setelah fungsi ditunda dengan 2 detik, cetak garis log, kemudian ditunda dengan 3 detik, dan kemudian menunda 4 detik, cetak baris log. Ini adalah hal yang sangat sederhana dalam bahasa pemrograman lain, tetapi lebih sulit untuk masuk ke JS, dan kode mungkin akan ditulis sebagai berikut:
var myfunc = function () {setTimeOut (function () {console.log ("log1"); setTimeout (function () {console.log ("log2"); setTimeout (function () {console.log ("log3");}, 4000);}, 3000);}, 2000);};Karena struktur panggilan balik multi-lapisan bersarang, struktur piramida khas terbentuk di sini. Jika logika bisnis lebih rumit, itu akan menjadi neraka panggilan balik yang menakutkan.
Jika Anda memiliki kesadaran yang lebih baik dan tahu cara mengekstrak fungsi sederhana, maka kode akan terlihat seperti ini:
var func1 = function () {setTimeOut (func2, 2000);}; var func2 = function () {console.log ("log1"); setTimeout (func3, 3000);}; var func3 = function () {console.log ("log2"); setTimeout (func4, 4000);}; var func4 = function () {console.log ("log3");};Ini terlihat sedikit lebih baik, tapi selalu terasa sedikit aneh. . . Nah, pada kenyataannya, level JS saya terbatas, jadi saya tidak bisa mengatakan mengapa saya tidak bisa menulis ini dengan baik. Jika Anda tahu mengapa ini tidak baik dan Anda menemukan janji, beri tahu saya.
Sekarang mari kita kembali ke intinya dan berbicara tentang hal janji itu.
Deskripsi janji
Tolong izinkan saya untuk mengutip deskripsi MDN tentang janji di sini:
Objek janji digunakan untuk perhitungan yang ditangguhkan dan perhitungan asinkron. Objek janji mewakili operasi yang belum selesai tetapi diharapkan akan selesai di masa depan.
Objek janji adalah proxy untuk nilai pengembalian, yang mungkin tidak diketahui ketika objek janji dibuat. Ini memungkinkan Anda untuk menentukan metode penanganan untuk keberhasilan atau kegagalan operasi asinkron. Ini memungkinkan metode asinkron untuk mengembalikan nilai seperti metode sinkron: metode asinkron mengembalikan objek janji yang berisi nilai pengembalian asli alih -alih nilai pengembalian asli.
Objek janji memiliki keadaan berikut:
• Tertunda: keadaan awal, tidak terpenuhi atau ditolak.
• Dipenuhi: Operasi yang berhasil.
• Ditolak: Operasi yang gagal.
Objek janji dengan keadaan yang tertunda dapat dikonversi ke keadaan terpenuhi dengan nilai keberhasilan atau keadaan yang ditolak dengan pesan kegagalan. Ketika status transisi, metode terikat untuk menjanjikan. Kemudian (pegangan fungsi) akan dipanggil. (Ketika mengikat metode, jika objek janji sudah dalam keadaan terpenuhi atau ditolak, metode yang sesuai akan segera dipanggil, sehingga tidak ada kondisi ras antara penyelesaian operasi asinkron dan metode pengikatannya.)
Untuk lebih banyak deskripsi dan contoh janji, silakan merujuk ke entri janji MDN atau entri janji MSDN.
Cobalah untuk memecahkan masalah kita dengan janji
Berdasarkan pemahaman janji di atas, kita tahu bahwa kita dapat menggunakannya untuk menyelesaikan masalah bahwa kode di balik panggilan balik multi-lapisan bersarang itu bodoh dan sulit dipertahankan. Dua tautan yang diberikan di atas sudah sangat jelas tentang sintaks dan parameter janji. Saya tidak akan mengulanginya di sini, cukup unggah kode.
Pertama -tama mari kita coba kasus yang relatif sederhana, yang hanya menjalankan penundaan dan panggilan balik sekali:
janji baru (function (res, rej) {console.log (date.now () + "Mulai setTimeout"); setTimeout (res, 2000);}). Kemudian (function () {console.log (date.now () + "call waktu kembali");});Tampaknya tidak ada perbedaan dari contoh di MSDN, dan hasil eksekusi adalah sebagai berikut:
$ node promistest.js1450194136374 Mulai setTimeOut1450194138391 HALAM PANGGILAN KEMBALI
Jadi jika kita ingin melakukan penundaan lain, maka saya bisa menulis ini:
janji baru (function (res, rej) {console.log (date.now () + "Mulai setTimeout 1"); setTimeout (res, 2000);}). Kemudian (function () {console.log (date.now () + "timeout 1 call back"); new janji (function (res, rej) {console.log. }). Kemudian (function () {console.log (date.now () + "Timeout 2 call back");})});Tampaknya berfungsi dengan benar juga:
$ node promistest.js1450194338710 Mulai setTimeout 11450194340720 Timeout 1 Hubungi Back1450194340720 Mulai SetTimeout 21450194343722 Batas waktu 2 Hubungi Kembali
Tapi kodenya terlihat bodoh dan lucu, bukan? Samar -samar membangun piramida lagi. Ini bertentangan dengan tujuan memperkenalkan janji.
Jadi apa masalahnya? Apa postur yang benar?
Jawabannya disembunyikan dalam nilai pengembalian fungsi saat itu dan fungsi panggilan balik fungsi yang lebih tinggi (atau dilengkapi).
Pertama -tama, fungsi saat itu akan mengembalikan variabel janji baru, dan Anda dapat menyebut fungsi variabel janji baru ini lagi, seperti ini:
janji baru (...). Lalu (...) .then (...). Lalu (...). Lalu (...). Lalu (...).
Jenis janji apa yang dikembalikan oleh fungsi saat itu tergantung pada nilai pengembalian dari panggilan balik yang dipenuhi.
Faktanya, Onfulfilled dapat mengembalikan variabel normal atau variabel janji lain.
Jika onfulfilled mengembalikan nilai normal, maka fungsi akan mengembalikan variabel janji default. Melaksanakan fungsi saat itu dari janji ini akan membuat janji itu segera memuaskan, dan fungsi yang lebih tinggi dieksekusi, dan parameter entri yang dipenuhi adalah nilai pengembalian dari yang sebelumnya dipenuhi.
Jika onfulfilled mengembalikan variabel janji, variabel janji itu akan digunakan sebagai nilai pengembalian fungsi kemudian.
Dokumen -dokumen tentang MDN dan MSDN tidak memiliki deskripsi positif yang jelas tentang rangkaian pengaturan ini untuk fungsi saat itu dan fungsi yang dipenuhi. Adapun dokumen ES6 resmi ECMASCRIPT 2015 (Edisi ke-6, ECMA-262). . . Saya benar -benar tidak dapat memahami level saya. Jika ada ahli yang dapat menjelaskan deskripsi dua nilai pengembalian dalam dokumen resmi, silakan tinggalkan pesan untuk nasihat! Lai Lai
Jadi di atas adalah permainan gratis saya, dan organisasi bahasa agak sulit untuk dijelaskan. Anda akan mengerti setelah membaca kode.
Pertama, kasus mengembalikan variabel normal:
janji baru (function (res, rej) {console.log (date.now () + "Mulai setTimeout 1"); setTimeout (res, 2000);}). Kemudian (function () {console.log (date.nownow () + "console" call "); return 1024;}). Lalu (function.wnow () + console." });Hasil eksekusi kode di atas adalah:
$ node promistest.js1450277122125 Mulai setTimeout 11450277124129 Batas waktu 1 hubungi kembali14502777124129 Pengembalian Terakhir Terakhir 1024
Agak menarik, benar, tapi itu bukan kuncinya. Kuncinya adalah bahwa fungsi yang lebih tinggi mengembalikan variabel janji, yang membuatnya nyaman bagi kita untuk memanggil beberapa proses asinkron secara berurutan. Misalnya, kita dapat mencoba melakukan dua operasi penundaan secara berurutan:
janji baru (function (res, rej) {console.log (date.now () + "Mulai setTimeout 1"); setTimeout (res, 2000);}). Kemudian (function () {console.log (date.now () + "timeout 1 call back"); return new janji (function (res, rej) {console.log. 3000);});}). Kemudian (function () {console.log (date.now () + "Timeout 2 call kembali");});Hasil eksekusi adalah sebagai berikut:
$ node promistest.js1450277510275 Mulai setTimeOut 11450277512276 Batas waktu 1 hubungi Back1450277512276 Mulai SetTimeout 21450277515327 Batas waktu 2
Jika Anda pikir ini tidak bagus, maka itu bukan masalah untuk melakukannya beberapa kali lagi:
janji baru (function (res, rej) {console.log (date.now () + "Mulai setTimeout 1"); setTimeout (res, 2000);}). Kemudian (function () {console.log (date.now () + "timeout 1 call back"); return new janji (function (res, rej) {console.log. 3000);});}). Kemudian (function () {console.log (date.now () + "timeout 2 call kembali"); return new janji (function (res, rej) {console.log (date.nown. 3 Panggilan kembali "); return new janji (function (res, rej) {console.log (date.now () +" Mulai setTimeout 4 "); setTimeout (res, 5000);});});}). Lalu (function () {console.log (Date.now () +" Timeout 4 call back ");});$ node promistest.js1450277902714 Mulai setTimeout 11450277904722 Batas waktu 1 hubungi kembali14502777904724 Mulai setTimeout 2145027777775 Batas waktu 21110777777777907725 MULAI 3147777777777777777777777777777907725 MULAI 3145027777777777777777777907725 SetTimeout 41450277916744 Timeout 4 Hubungi Kembali
Dapat dilihat bahwa beberapa fungsi callback yang tertunda diatur dengan cara yang tertib, dan tidak ada struktur seperti piramida yang populer. Meskipun kode tersebut menyebut proses asinkron, sepertinya semuanya terdiri dari proses sinkron. Ini adalah janji manfaat yang membawa kita.
Jika Anda memiliki kebiasaan baik untuk menyuling kode verbose menjadi fungsi yang terpisah, itu akan lebih indah:
function timeout1 () {return new janji (function (res, rej) {console.log (date.now () + "start timeout1"); setTimeout (res, 2000);});} function timeout2 () {return new janji (function (res, rej) {console.log (date.now a () {function (res, res, resole. timeout3 () {return new janji (function (res, rej) {console.log (date.now () + "start timeout2"); setTimeout (res, 3000);});} function timeout3 () {return new janji (function (res, rej) {console.log (date.now.now () "{function function (res, res, console.log (date.now.now ()" {function function (res, res, console. timeout4 () {return new janji (function (res, rej) {console.log (date.now () + "start timeout4"); setTimeout (res, 5000);});} timeout1 () .then (timeout2) .then (timeout3) .then (timeout4) .then (function () {console. });$ node promistest.js1450278983342 Mulai timeout11450278985343 MULAI TIMEOUT2145027898351 MULAI TIMEOUT3145027892356 MULAI TIMEOUT4145027897370 CALLBACKBack
Selanjutnya, kita dapat terus mempelajari masalah lewat dalam parameter yang masuk dari fungsi yang dipenuhi.
Kita sudah tahu bahwa jika fungsi onfulfilled sebelumnya mengembalikan nilai normal, maka nilai ini adalah parameter entri dari fungsi onfulfilled; Lalu jika sebelumnya yang telah dipenuhi dengan variabel janji, dari mana parameter entri berasal dari yang dipenuhi?
Jawabannya adalah bahwa parameter entri dari fungsi yang dipenuhi ini adalah nilai yang dilewati ketika fungsi tekad dipanggil pada janji sebelumnya.
Saya tidak bisa menerima lompatan untuk sementara waktu, kan? Mari kita lakukan dengan baik.
Pertama -tama, apa fungsinya janji. Menggunakan pernyataan dari Zou Zou di atas MDN
Selesaikan objek janji dengan nilai nilai keberhasilan. Jika nilainya berkelanjutan (dapat kemudian, yaitu dengan metode itu), objek janji yang dikembalikan akan "mengikuti" nilai
Singkatnya, ini adalah panggilan balik ketika panggilan asinkron berhasil.
Mari kita lihat seperti apa panggilan balik itu di antarmuka asinkron normal. Ambil fs.readfile (file [, opsi], callback) di nodeJs misalnya. Contoh panggilan khasnya adalah sebagai berikut
fs.readfile ('/etc/passwd', function (err, data) {if (err) throw err; console.log (data);});Karena untuk fungsi fs.readfile, apakah berhasil atau gagal, itu akan memanggil callback function callback, jadi panggilan balik ini menerima dua parameter, yaitu deskripsi pengecualian tentang kegagalan err dan data hasil pengembalian tentang keberhasilan.
Jadi jika kita menggunakan janji untuk merekonstruksi contoh file membaca ini, bagaimana kita harus menulisnya?
Pertama, merangkum fungsi fs.readfile:
fungsi readFile (fileName) {return new janji (function, resolve, reject) {fs.readfile (fileName, function (err, data) {if (err) {reject (err);} else {resolve (data);});});});}Yang kedua adalah panggilan:
readFile ('thefile.txt'). Kemudian (function (data) {console.log (data);}, function (err) {throw err;});Bayangkan di mana konten file biasanya ditempatkan di antarmuka panggilan sinkron membaca file dalam bahasa lain? Apakah nilai return fungsi benar? Jawabannya keluar, apa entri ginseng dari tekad ini? Ini adalah nilai pengembalian ketika panggilan asinkron berhasil.
Dengan konsep ini, tidak sulit untuk memahami "parameter input dari fungsi yang diikat adalah nilai yang dilewati ketika memanggil fungsi tekad dalam janji sebelumnya". Karena tugas yang dipenuhi adalah memproses hasil setelah panggilan asinkron sebelumnya berhasil.
Sayangnya akhirnya diluruskan. . .
Meringkaskan
Tolong izinkan saya menggunakan sepotong kode untuk meringkas poin -poin utama yang dijelaskan dalam artikel ini:
fungsi callp1 () {console.log (date.now () + "Mulai callp1"); return new janji (function (res, rej) {setTimeout (res, 2000);});} function callp2 () {console.log (date.now () + "Mulai callp2"); return new janji (function (res, rej) {setTimeOut (function () {res ({arg1: 4, arg2: "arg2 value"});}, 3000);});} function callp3 (arg) {console.log (date.now ()) + "start callp3 dengan arg =" + arg); return new janji (function (res, rej) {setTimeOut (function () {res ("callp3");}, arg * 1000);});} callp1 (). Kemudian (fungsi () {console.log (date.now dengan retsole (rets) {calloG) {caller. = " + Json.stringify (ret)); return callp3 (ret.arg1);}). Kemudian (function (ret) {console.log (date.now () +" callp3 kembali dengan ret value = " + ret);}) $ node promistest.js1450191479575 Mulai callp11450191481597 callp1 return1450191481599 Mulai callp21450191484605 callp2 kembali dengan nilai ret = "arg1": 4, "arg2": "arg2" {"arg1": 4, "arg2": "arg2" {"arg1" 41450191488610 callp3 kembali dengan nilai ret = callp3Pengalaman belajar sederhana di atas menggunakan janji untuk menyelesaikan panggilan asinkron multi-lapisan adalah semua konten yang saya bagikan dengan Anda. Saya harap Anda dapat memberi Anda referensi dan saya harap Anda dapat mendukung wulin.com lebih lanjut.