Saya baru-baru ini mengalami situasi tertentu beberapa kali, yang tidak saya ketahui cara menyelesaikannya dengan benar. Asumsikan kode berikut:
somethingAsync()
.then( afterSomething )
.then( afterSomethingElse )
function afterSomething( amazingData ) {
return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
}
Sekarang situasi mungkin muncul di mana saya ingin memiliki akses amazingData
masuk afterSomethingElse
.
Salah satu solusi yang jelas adalah mengembalikan array atau hash dari afterSomething
, karena, Anda hanya dapat mengembalikan satu nilai dari suatu fungsi. Tetapi saya bertanya-tanya apakah ada cara untuk afterSomethingElse
menerima 2 parameter dan menjalankannya juga, karena itu tampaknya jauh lebih mudah untuk didokumentasikan dan dipahami.
Saya hanya bertanya-tanya tentang kemungkinan ini karena ada Q.spread
, yang melakukan sesuatu yang mirip dengan apa yang saya inginkan.
javascript
promise
q
Der Hochstapler
sumber
sumber
Jawaban:
Anda tidak dapat menyelesaikan suatu janji dengan beberapa properti seperti halnya Anda tidak dapat mengembalikan beberapa nilai dari suatu fungsi . Promise secara konseptual merepresentasikan nilai dari waktu ke waktu, jadi meskipun Anda bisa merepresentasikan nilai komposit, Anda tidak bisa memasukkan beberapa nilai dalam sebuah promise.
Promise secara inheren diselesaikan dengan satu nilai - ini adalah bagian dari cara kerja Q, cara kerja spesifikasi Promises / A +, dan cara kerja abstraksi .
Yang paling dekat yang bisa Anda dapatkan adalah menggunakan
Q.spread
dan mengembalikan array atau menggunakan ES6 destructuring jika didukung atau Anda ingin menggunakan alat transpilasi seperti BabelJS.Adapun untuk meneruskan konteks ke rantai janji, silakan lihat kanonik Bergi yang sangat baik tentang itu .
sumber
.spread()
menampilkan Bluebird seperti ini di jawaban terkait ini: stackoverflow.com/a/22776850/1624862Promise.all([a, b, c]).then(function(x, y, z) {...})
bekerja dengan benar di semua mesin Javascript modern dengan x, y, dan z mengevaluasi ke nilai yang diselesaikan dari a, b, dan c. Jadi lebih akurat untuk mengatakan bahwa bahasa tidak membiarkan Anda melakukannya dengan mudah (atau masuk akal) dari kode pengguna (karena Anda dapat mengembalikan Promise langsung dari klausa then, Anda dapat menggabungkan nilai Anda dalam promise dan kemudian menggabungkannya dengan Promise .all () untuk mendapatkan perilaku yang diinginkan, meskipun dengan cara yang berbelit-belit).Promise.all
dipenuhi dengan array . DalamPromise.all([a,b]).then((a, b) =>
b
adalahundefined
. Itulah mengapa Anda perlu melakukan.then(([a, b]) =>
yang merupakan tugas yang merusakAnda hanya dapat mengirimkan satu nilai, tetapi dapat berupa larik dengan nilai kelipatan di dalamnya, sebagai contoh:
function step1(){ let server = "myserver.com"; let data = "so much data, very impresive"; return Promise.resolve([server, data]); }
di sisi lain, Anda dapat menggunakan ekspresi penghancuran untuk ES2015 untuk mendapatkan nilai individual.
function step2([server, data]){ console.log(server); // print "myserver.com" console.log(data); // print "so much data, very impresive" return Promise.resolve("done"); }
untuk menyebut keduanya janji, merangkainya:
step1() .then(step2) .then((msg)=>{ console.log(msg); // print "done" })
sumber
function step2([server, data]) { …
- dengan cara itu Anda juga menghindari untuk menetapkan ke global implisit. Dan Anda benar-benar harus menggunakanreturn
atauPromise.resolve
, bukannew Promise
konstruktor dalam contoh Anda.Anda dapat mengembalikan objek yang berisi kedua nilai - tidak ada yang salah dengan itu.
Strategi lain adalah mempertahankan nilai, melalui closure, alih-alih meneruskannya:
somethingAsync().then(afterSomething); function afterSomething(amazingData) { return processAsync(amazingData).then(function (processedData) { // both amazingData and processedData are in scope here }); }
Bentuk sebaris sepenuhnya daripada sebagian (setara, bisa dibilang lebih konsisten):
somethingAsync().then(function (amazingData) { return processAsync(amazingData).then(function (processedData) { // both amazingData and processedData are in scope here }); }
sumber
then
dalam yang lainthen
? Ini bukan anti-pola ?Dua hal yang dapat Anda lakukan, mengembalikan sebuah benda
somethingAsync() .then( afterSomething ) .then( afterSomethingElse ); function processAsync (amazingData) { //processSomething return { amazingData: amazingData, processedData: processedData }; } function afterSomething( amazingData ) { return processAsync( amazingData ); } function afterSomethingElse( dataObj ) { let amazingData = dataObj.amazingData, processedData = dataObj.proccessedData; }
Gunakan ruang lingkup!
var amazingData; somethingAsync() .then( afterSomething ) .then( afterSomethingElse ) function afterSomething( returnedAmazingData ) { amazingData = returnedAmazingData; return processAsync( amazingData ); } function afterSomethingElse( processedData ) { //use amazingData here }
sumber
Menurut saya, inilah yang harus Anda lakukan.
memisahkan rantai
Karena kedua fungsi akan menggunakan amazingData , maka masuk akal untuk memilikinya dalam fungsi khusus. Saya biasanya melakukan itu setiap kali saya ingin menggunakan kembali beberapa data, jadi itu selalu ada sebagai fungsi arg.
Sebagai contoh Anda menjalankan beberapa kode, saya anggap semuanya dideklarasikan di dalam sebuah fungsi. Saya akan menyebutnya toto () . Kemudian kita akan memiliki fungsi lain yang akan menjalankan afterSomething () dan afterSomethingElse () .
function toto() { return somethingAsync() .then( tata ); }
Anda juga akan melihat saya menambahkan pernyataan pengembalian karena biasanya ini adalah cara untuk mengikuti Promises - Anda selalu mengembalikan janji sehingga kami dapat terus merangkai jika diperlukan. Di sini, sesuatuAsync () akan menghasilkan amazingData dan akan tersedia di mana saja di dalam fungsi baru.
Sekarang, seperti apa fungsi baru ini yang biasanya bergantung pada prosesAsync () juga asynchronous ?
processAsync tidak asynchronous
Tidak ada alasan untuk memperumit masalah jika processAsync () tidak asynchronous. Beberapa kode sekuensial lama yang bagus akan berhasil.
function tata( amazingData ) { var processed = afterSomething( amazingData ); return afterSomethingElse( amazingData, processed ); } function afterSomething( amazingData ) { return processAsync( amazingData ); } function afterSomethingElse( amazingData, processedData ) { }
Perhatikan bahwa tidak masalah jika afterSomethingElse () melakukan sesuatu yang asinkron atau tidak. Jika ya, janji akan dikembalikan dan rantai dapat berlanjut. Jika tidak, maka nilai hasil akan dikembalikan. Tetapi karena fungsinya dipanggil dari a then () , nilainya akan tetap digabungkan menjadi janji (setidaknya dalam Javascript mentah).
processAsync asynchronous
Jika processAsync () asynchronous, kode akan terlihat sedikit berbeda. Di sini kami menganggap afterSomething () dan afterSomethingElse () tidak akan digunakan kembali di tempat lain.
function tata( amazingData ) { return afterSomething() .then( afterSomethingElse ); function afterSomething( /* no args */ ) { return processAsync( amazingData ); } function afterSomethingElse( processedData ) { /* amazingData can be accessed here */ } }
Sama seperti sebelumnya untuk afterSomethingElse () . Ini bisa asinkron atau tidak. Sebuah janji akan dikembalikan, atau nilai yang dibungkus menjadi janji yang diselesaikan.
Gaya pengkodean Anda cukup dekat dengan apa yang biasa saya lakukan, itulah mengapa saya menjawab bahkan setelah 2 tahun. Saya bukan penggemar berat fungsi anonim di mana-mana. Saya merasa sulit untuk membaca. Kalaupun hal itu cukup umum di masyarakat. Seperti kita mengganti neraka panggilan dengan api penyucian janji .
Saya juga suka membuat nama fungsi di kemudian pendek. Mereka hanya akan didefinisikan secara lokal. Dan sebagian besar waktu mereka akan memanggil fungsi lain yang ditentukan di tempat lain - sangat dapat digunakan kembali - untuk melakukan pekerjaan itu. Saya bahkan melakukan itu untuk fungsi dengan hanya 1 parameter, jadi saya tidak perlu memasukkan fungsi masuk dan keluar saat saya menambahkan / menghapus parameter ke tanda tangan fungsi.
Contoh makan
Berikut ini contohnya:
function goingThroughTheEatingProcess(plenty, of, args, to, match, real, life) { return iAmAsync() .then(chew) .then(swallow); function chew(result) { return carefullyChewThis(plenty, of, args, "water", "piece of tooth", result); } function swallow(wine) { return nowIsTimeToSwallow(match, real, life, wine); } } function iAmAsync() { return Promise.resolve("mooooore"); } function carefullyChewThis(plenty, of, args, and, some, more) { return true; } function nowIsTimeToSwallow(match, real, life, bobool) { }
Jangan terlalu fokus pada Promise.resolve () . Ini hanyalah cara cepat untuk membuat janji yang sudah pasti. Apa yang saya mencoba untuk mencapai dengan ini adalah untuk memiliki semua kode saya berjalan di satu lokasi - hanya di bawah thens . Semua fungsi lainnya dengan nama yang lebih deskriptif dapat digunakan kembali.
Kelemahan dari teknik ini adalah teknik ini mendefinisikan banyak fungsi. Tapi itu rasa sakit yang perlu saya khawatirkan untuk menghindari fungsi anonim di mana-mana. Dan apa risikonya: tumpukan melimpah? (lelucon!)
Menggunakan array atau objek seperti yang didefinisikan dalam jawaban lain juga akan berfungsi. Yang satu ini adalah jawaban yang diajukan oleh Kevin Reid .
Anda juga dapat menggunakan bind () atau Promise.all () . Perhatikan bahwa mereka masih akan meminta Anda untuk membagi kode Anda.
menggunakan bind
Jika Anda ingin agar fungsi Anda dapat digunakan kembali tetapi tidak benar-benar perlu menyimpan apa yang ada di dalamnya dalam waktu yang sangat singkat, Anda dapat menggunakan bind () .
function tata( amazingData ) { return afterSomething( amazingData ) .then( afterSomethingElse.bind(null, amazingData) ); } function afterSomething( amazingData ) { return processAsync( amazingData ); } function afterSomethingElse( amazingData, processedData ) { }
Sederhananya, bind () akan menambahkan daftar args (kecuali yang pertama) ke fungsi saat dipanggil.
menggunakan Promise.all
Di postingan Anda, Anda menyebutkan penggunaan spread () . Saya tidak pernah menggunakan kerangka kerja yang Anda gunakan, tetapi inilah cara Anda dapat menggunakannya.
Beberapa percaya Promise.all () adalah solusi untuk semua masalah, jadi saya rasa pantas untuk disebutkan.
function tata( amazingData ) { return Promise.all( [ amazingData, afterSomething( amazingData ) ] ) .then( afterSomethingElse ); } function afterSomething( amazingData ) { return processAsync( amazingData ); } function afterSomethingElse( args ) { var amazingData = args[0]; var processedData = args[1]; }
Anda bisa meneruskan data ke Promise.all () - perhatikan keberadaan array - selama promise, tetapi pastikan tidak ada promise yang gagal jika tidak akan berhenti diproses.
Dan alih-alih menentukan variabel baru dari argumen args , Anda harus bisa menggunakan spread () daripada then () untuk semua jenis pekerjaan yang mengagumkan.
sumber
Cukup buat sebuah objek dan ekstrak argumen dari objek tersebut.
let checkIfNumbersAddToTen = function (a, b) { return new Promise(function (resolve, reject) { let c = parseInt(a)+parseInt(b); let promiseResolution = { c:c, d : c+c, x : 'RandomString' }; if(c===10){ resolve(promiseResolution); }else { reject('Not 10'); } }); };
Tarik argumen dari promiseResolution.
checkIfNumbersAddToTen(5,5).then(function (arguments) { console.log('c:'+arguments.c); console.log('d:'+arguments.d); console.log('x:'+arguments.x); },function (failure) { console.log(failure); });
sumber
Apa pun yang Anda hasilkan dari sebuah janji akan dibungkus menjadi janji untuk dibuka pada
.then()
tahap berikutnya .Ini menjadi menarik saat Anda perlu mengembalikan satu atau beberapa promise di samping satu atau beberapa nilai sinkron seperti;
Promise.resolve([Promise.resolve(1), Promise.resolve(2), 3, 4]) .then(([p1,p2,n1,n2]) => /* p1 and p2 are still promises */);
Dalam kasus ini, penting untuk menggunakan
Promise.all()
untuk mendapatkanp1
danp2
membuka bungkusan janji pada.then()
tahap berikutnya sepertiPromise.resolve(Promise.all([Promise.resolve(1), Promise.resolve(2), 3, 4])) .then(([p1,p2,n1,n2]) => /* p1 is 1, p2 is 2, n1 is 3 and n2 is 4 */);
sumber
Anda dapat memeriksa Observable yang diwakili oleh Rxjs , memungkinkan Anda mengembalikan lebih dari satu nilai.
sumber
Cukup kembalikan tupel:
async add(dto: TDto): Promise<TDto> { console.log(`${this.storeName}.add(${dto})`); return firebase.firestore().collection(this.dtoName) .withConverter<TDto>(this.converter) .add(dto) .then(d => [d.update(this.id, d.id), d.id] as [any, string]) .then(x => this.get(x[1]));
}
sumber