Saya telah merestrukturisasi kode saya menjadi janji , dan membangun rantai janji datar panjang yang indah , yang terdiri dari beberapa .then()
panggilan balik. Pada akhirnya saya ingin mengembalikan beberapa nilai komposit, dan perlu mengakses beberapa hasil janji menengah . Namun nilai-nilai resolusi dari tengah urutan tidak dalam cakupan di panggilan balik terakhir, bagaimana cara mengaksesnya?
function getExample() {
return promiseA(…).then(function(resultA) {
// Some processing
return promiseB(…);
}).then(function(resultB) {
// More processing
return // How do I gain access to resultA here?
});
}
javascript
, itu relevan dalam bahasa lain. Saya hanya menggunakan jawaban "break the chain" di java dan jdeferredJawaban:
Hancurkan rantai
Ketika Anda perlu mengakses nilai-nilai perantara dalam rantai Anda, Anda harus membagi rantai Anda menjadi satu bagian yang Anda butuhkan. Alih-alih melampirkan satu panggilan balik dan entah bagaimana mencoba menggunakan parameternya beberapa kali, lampirkan beberapa panggilan balik ke janji yang sama - di mana pun Anda membutuhkan nilai hasil. Jangan lupa, janji hanya mewakili (proksi) nilai masa depan ! Di samping mendapatkan satu janji dari yang lain dalam rantai linier, gunakan combinator janji yang diberikan kepada Anda oleh perpustakaan Anda untuk membangun nilai hasil.
Ini akan menghasilkan aliran kontrol yang sangat mudah, komposisi fungsi yang jelas, dan oleh karenanya modularisasi mudah.
Alih-alih destructuring parameter di panggil balik setelah
Promise.all
hanya menjadi tersedia dengan ES6, di ES5 yangthen
panggilan akan digantikan oleh metode pembantu bagus yang disediakan oleh banyak perpustakaan janji ( Q , Bluebird , ketika , ...):.spread(function(resultA, resultB) { …
.Bluebird juga memiliki
join
fungsi khusus untuk menggantikan kombinasiPromise.all
+ ituspread
dengan konstruk yang lebih sederhana (dan lebih efisien):sumber
promiseA
danpromiseB
apakah fungsi (janji kembali) di sini.spread
sangat berguna dalam pola ini. Untuk solusi yang lebih modern lihat jawaban yang diterima. Namun, saya sudah memperbarui jawaban passthrough eksplisit , dan benar-benar tidak ada alasan untuk tidak memperbarui yang satu ini juga.Harmony ECMAScript
Tentu saja, masalah ini juga diakui oleh perancang bahasa. Mereka melakukan banyak pekerjaan dan proposal fungsi async akhirnya berhasil
ECMAScript 8
Anda tidak memerlukan satu
then
fungsi pemanggilan atau panggilan balik lagi, seperti pada fungsi asinkron (yang mengembalikan janji ketika dipanggil), Anda cukup menunggu janji untuk diselesaikan secara langsung. Ini juga menampilkan struktur kontrol sewenang-wenang seperti kondisi, loop dan klausa coba-tangkap, tetapi demi kenyamanan kita tidak membutuhkannya di sini:ECMAScript 6
Sementara kami menunggu ES8, kami sudah menggunakan sintaks yang sangat mirip. ES6 hadir dengan fungsi generator , yang memungkinkan untuk memecah eksekusi menjadi beberapa bagian dengan
yield
kata kunci yang ditempatkan secara sewenang-wenang . Irisan tersebut dapat dijalankan satu sama lain, secara mandiri, bahkan secara tidak sinkron - dan itulah yang kami lakukan ketika kami ingin menunggu resolusi yang dijanjikan sebelum menjalankan langkah berikutnya.Ada perpustakaan khusus (seperti co atau task.js ), tetapi juga banyak perpustakaan janji memiliki fungsi pembantu ( Q , Bluebird , kapan , ...) yang melakukan eksekusi langkah-demi-langkah async ini untuk Anda ketika Anda memberi mereka fungsi generator yang menghasilkan janji.
Ini berhasil di Node.js sejak versi 4.0, juga beberapa browser (atau edisi dev mereka) mendukung sintaks generator relatif lebih awal.
ECMAScript 5
Namun, jika Anda ingin / perlu kompatibel-mundur Anda tidak dapat menggunakannya tanpa transpiler. Baik fungsi generator dan fungsi async didukung oleh tooling saat ini, lihat misalnya dokumentasi Babel pada generator dan fungsi async .
Dan kemudian, ada juga banyak bahasa kompilasi-ke-JS yang didedikasikan untuk memudahkan pemrograman asinkron. Mereka biasanya menggunakan sintaks mirip dengan
await
, (misalnya Iced CoffeeScript ), tetapi ada juga orang lain yang menampilkan Haskell-sepertido
-notation (misalnya LatteJs , monadic , PureScript atau LispyScript ).sumber
getExample
masih merupakan fungsi yang mengembalikan janji, berfungsi seperti fungsi di jawaban lain, tetapi dengan sintaks yang lebih bagus. Anda dapatawait
panggilan dalamasync
fungsi lain , atau Anda dapat.then()
membuat hasilnya.steps.next().value.then(steps.next)...
tetapi itu tidak berhasil.Inspeksi sinkron
Menetapkan nilai-nilai yang dijanjikan untuk variabel di kemudian hari dan kemudian mendapatkan nilainya melalui inspeksi sinkron. Contohnya menggunakan
.value()
metode bluebird tetapi banyak perpustakaan menyediakan metode serupa.Ini dapat digunakan untuk nilai sebanyak yang Anda suka:
sumber
Penutupan bersarang (dan)
Menggunakan penutupan untuk mempertahankan ruang lingkup variabel (dalam kasus kami, parameter fungsi panggilan balik sukses) adalah solusi JavaScript alami. Dengan janji-janji, kita dapat secara sewenang-wenang mengumpulkan dan meratakan
.then()
panggilan balik - mereka secara semantik setara, kecuali untuk ruang lingkup bagian dalam.Tentu saja, ini membangun lekukan piramida. Jika lekukan menjadi terlalu besar, Anda masih dapat menerapkan alat-alat lama untuk melawan piramida malapetaka : modularisasi, gunakan fungsi tambahan bernama, dan ratakan rantai janji segera setelah Anda tidak memerlukan variabel lagi.
Secara teori, Anda selalu dapat menghindari lebih dari dua tingkat bersarang (dengan membuat semua penutupan eksplisit), dalam praktiknya gunakan sebanyak yang masuk akal.
Anda juga dapat menggunakan fungsi pembantu untuk aplikasi parsial semacam ini , seperti
_.partial
dari Garis Bawah / lodash atau metode asli.bind()
, untuk lebih jauh mengurangi indentasi:sumber
bind
fungsi di Monads. Haskell menyediakan sintaksis gula (jangan-notasi) agar terlihat seperti sintaksis async / tunggu.Pass-through eksplisit
Mirip dengan bersarangnya callback, teknik ini bergantung pada penutupan. Namun, rantai tetap datar - alih-alih hanya melewati hasil terbaru, beberapa objek negara dilewatkan untuk setiap langkah. Objek keadaan ini mengakumulasikan hasil dari tindakan sebelumnya, menyerahkan semua nilai yang akan dibutuhkan nanti ditambah hasil dari tugas saat ini.
Di sini, panah kecil itu
b => [resultA, b]
adalah fungsi yang menutupresultA
, dan meneruskan array dari kedua hasil ke langkah berikutnya. Yang menggunakan sintaksis perusak parameter untuk memecahnya dalam variabel tunggal lagi.Sebelum penghancuran tersedia dengan ES6, metode pembantu yang bagus disebut
.spread()
disediakan oleh banyak perpustakaan janji ( Q , Bluebird , kapan , ...). Dibutuhkan fungsi dengan beberapa parameter - satu untuk setiap elemen array - untuk digunakan sebagai.spread(function(resultA, resultB) { …
.Tentu saja, penutupan yang diperlukan di sini dapat lebih disederhanakan oleh beberapa fungsi pembantu, misalnya
Atau, Anda dapat menggunakan
Promise.all
untuk menghasilkan janji untuk array:Dan Anda mungkin tidak hanya menggunakan array, tetapi objek kompleks yang sewenang-wenang. Misalnya, dengan
_.extend
atauObject.assign
dalam fungsi pembantu yang berbeda:Sementara pola ini menjamin rantai datar dan objek keadaan eksplisit dapat meningkatkan kejelasan, itu akan menjadi membosankan untuk rantai panjang. Terutama ketika Anda membutuhkan negara hanya secara sporadis, Anda masih harus melewati setiap langkah. Dengan antarmuka tetap ini, panggilan balik tunggal dalam rantai agak erat digabungkan dan tidak fleksibel untuk diubah. Itu membuat anjak keluar satu langkah lebih sulit, dan callback tidak dapat dipasok langsung dari modul lain - mereka selalu harus dibungkus dengan kode boilerplate yang peduli dengan keadaan. Fungsi pembantu abstrak seperti di atas dapat sedikit meredakan rasa sakit, tetapi akan selalu ada.
sumber
Promise.all
seharusnya didorong (itu tidak akan bekerja di ES6 ketika merusak akan menggantikannya dan beralih.spread
ke athen
memberi orang hasil yang sering tak terduga. Pada penambahan - saya tidak yakin mengapa Anda perlu menggunakan augment - menambahkan hal-hal pada prototipe janji bukanlah cara yang dapat diterima untuk memperpanjang janji ES6 yang seharusnya diperpanjang dengan subklas (yang saat ini tidak didukung)Promise.all
"? Tidak ada metode dalam jawaban ini yang akan putus dengan ES6. Mengalihkanspread
ke merusakthen
harus tidak memiliki masalah juga. Re .prototype.augment: Saya tahu seseorang akan memperhatikannya, saya hanya suka menjelajahi kemungkinan - akan mengeditnya.return [x,y]; }).spread(...
bukannyareturn Promise.all([x, y]); }).spread(...
yang tidak akan berubah ketika bertukar menyebar untuk es6 merusak gula dan juga tidak akan menjadi kasus tepi aneh di mana janji memperlakukan kembali array berbeda dari yang lain.Keadaan kontekstual yang dapat berubah
Solusi trivial (tetapi tidak tepat dan agak error) adalah dengan hanya menggunakan variabel lingkup yang lebih tinggi (yang semua akses balik dalam rantai memiliki akses) dan menulis nilai hasil kepada mereka ketika Anda mendapatkannya:
Alih-alih banyak variabel orang mungkin juga menggunakan objek (awalnya kosong), di mana hasilnya disimpan sebagai properti yang dibuat secara dinamis.
Solusi ini memiliki beberapa kelemahan:
Perpustakaan Bluebird mendorong penggunaan objek yang diteruskan, menggunakan metode mereka
bind()
untuk menetapkan objek konteks ke rantai janji. Ini akan dapat diakses dari setiap fungsi panggilan balik melaluithis
kata kunci yang tidak dapat digunakan . Sementara properti objek lebih rentan terhadap kesalahan ketik yang tidak terdeteksi daripada variabel, polanya cukup pintar:Pendekatan ini dapat dengan mudah disimulasikan di perpustakaan janji yang tidak mendukung .bind (meskipun dengan cara yang agak lebih bertele-tele dan tidak dapat digunakan dalam ekspresi):
sumber
.bind()
tidak perlu untuk mencegah kebocoran memoriPutaran yang kurang keras pada "Keadaan kontekstual yang dapat berubah"
Menggunakan objek yang dicakup secara lokal untuk mengumpulkan hasil antara dalam rantai janji adalah pendekatan yang masuk akal untuk pertanyaan yang Anda ajukan. Pertimbangkan cuplikan berikut:
sumber
Promise
antipattern konstruktor !Node 7.4 sekarang mendukung panggilan async / menunggu dengan bendera harmoni.
Coba ini:
dan jalankan file dengan:
node --harmony-async-await getExample.js
Sesederhana mungkin!
sumber
Hari ini, saya juga harus memenuhi beberapa pertanyaan seperti Anda. Akhirnya, saya menemukan solusi yang baik dengan pertanyaan itu, sederhana dan bagus untuk dibaca. Saya harap ini dapat membantu Anda.
Menurut cara-untuk-rantai-javascript-janji
ok, mari kita lihat kodenya:
sumber
.then
dipanggil, tetapi hasil dari sebelumnya. MisalnyathirdPromise
mengakses hasil darifirstPromise
.Jawaban lain, menggunakan
babel-node
versi <6Menggunakan
async - await
npm install -g [email protected]
example.js:
Lalu, jalankan
babel-node example.js
dan voila!sumber
Saya tidak akan menggunakan pola ini dalam kode saya sendiri karena saya bukan penggemar menggunakan variabel global. Namun, dalam keadaan darurat itu akan berhasil.
Pengguna adalah model luwak resmi.
sumber
globalVar
sama sekali, lakukan sajaUser.findAsync({}).then(function(users){ console.log(users); mongoose.connection.close() });
?Jawaban lain, menggunakan nsynjs pelaksana berurutan :
Pembaruan: menambahkan contoh kerja
sumber
Saat menggunakan bluebird, Anda dapat menggunakan
.bind
metode untuk berbagi variabel dalam rantai janji:silakan periksa tautan ini untuk informasi lebih lanjut:
http://bluebirdjs.com/docs/api/promise.bind.html
sumber
cara mudah: D
sumber
Saya pikir Anda dapat menggunakan hash RSVP.
Sesuatu seperti di bawah ini:
sumber
Promise.all
solusinya , hanya dengan objek bukan array.Larutan:
Anda dapat menempatkan nilai-nilai perantara dalam lingkup fungsi nanti 'lalu' secara eksplisit, dengan menggunakan 'bind'. Ini adalah solusi bagus yang tidak perlu mengubah cara kerja Janji, dan hanya membutuhkan satu atau dua baris kode untuk menyebarkan nilai-nilai seperti halnya kesalahan sudah diperbanyak.
Ini adalah contoh lengkapnya:
Solusi ini dapat dipanggil sebagai berikut:
(Catatan: versi yang lebih kompleks dan lengkap dari solusi ini telah diuji, tetapi bukan versi contoh ini, sehingga dapat memiliki bug.)
sumber
async
Sayaawait
masih berarti menggunakan janji. Yang mungkin Anda tinggalkan adalahthen
panggilan dengan panggilan balik.Apa yang saya pelajari tentang janji adalah menggunakannya hanya sebagai nilai pengembalian menghindari merujuk jika memungkinkan. sintaks async / await sangat praktis untuk itu. Hari ini semua browser dan node terbaru mendukungnya: https://caniuse.com/#feat=async-functions , adalah perilaku sederhana dan kodenya seperti membaca kode sinkron, lupakan panggilan balik ...
Dalam kasus yang saya perlu rujuk janji adalah ketika penciptaan dan resolusi terjadi di tempat-tempat independen / tidak terkait. Jadi daripada asosiasi buatan dan mungkin pendengar acara hanya untuk menyelesaikan janji "jauh", saya lebih suka mengekspos janji sebagai Ditangguhkan, yang diterapkan oleh kode berikut ini dalam es5 yang valid
diubah bentuk dari proyek naskah saya:
https://github.com/cancerberoSgx/misc-utils-of-mine/blob/2927c2477839f7b36247d054e7e50abe8a41358b/misc-utils-of-mine-generic/src/promise.ts#L31
Untuk kasus yang lebih kompleks saya sering menggunakan utilitas janji orang kecil ini tanpa dependensi diuji dan diketik. p-map telah berguna beberapa kali. Saya pikir dia membahas sebagian besar kasus penggunaan:
https://github.com/sindresorhus?utf8=%E2%9C%93&tab=repositories&q=promise&type=source&language=
sumber