Apa perbedaan antara Masa Depan dan Janji?

275

Apa perbedaan antara Futuredan Promise?
Mereka berdua bertindak seperti pengganti untuk hasil masa depan, tetapi di mana perbedaan utama?

pengguna1170330
sumber
100
Anda dapat membuat Promisedan terserah Anda untuk menyimpannya. Ketika orang lain membuat Anda berjanji, Anda harus menunggu untuk melihat apakah mereka menghargainyaFuture
Kevin Wright
1
wikipedia Berjangka dan janji
mulai
30
Salah satu artikel Wikipedia paling tidak membantu yang pernah saya baca
Fulluphigh

Jawaban:

146

Menurut diskusi ini , Promiseakhirnya telah dipanggil CompletableFutureuntuk dimasukkan dalam Java 8, dan javadoc-nya menjelaskan:

Masa Depan yang dapat diselesaikan secara eksplisit (menetapkan nilai dan statusnya), dan dapat digunakan sebagai CompletionStage, mendukung fungsi dan tindakan yang bergantung yang memicu penyelesaiannya.

Contoh juga diberikan dalam daftar:

f.then((s -> aStringFunction(s)).thenAsync(s -> ...);

Perhatikan bahwa API final sedikit berbeda tetapi memungkinkan eksekusi asinkron serupa:

CompletableFuture<String> f = ...;
f.thenApply(this::modifyString).thenAccept(System.out::println);
assylias
sumber
78
Ini bukan salah Anda Assylias, tetapi ekstrak javadoc membutuhkan perbaikan serius oleh penulis teknologi yang layak. Pada pembacaan kelima saya, saya bisa mulai menghargai apa yang ingin dikatakannya ... dan saya sampai pada hal ini dengan pemahaman tentang masa depan dan janji yang sudah ada!
Beetroot-Beetroot
2
@ Beetroot-Beetroot sepertinya sudah terjadi sekarang.
herman
1
@herman Terima kasih - Saya telah memperbarui tautan untuk menunjuk ke versi terakhir javadoc.
assylias
7
@ Beetroot-Beetroot Anda harus memeriksa dokumen untuk metode Exceptionally. Itu akan membuat puisi yang indah, tetapi merupakan kegagalan luar biasa dari dokumentasi yang dapat dibaca.
Fullup
4
Bagi siapa pun yang bertanya, @Fulluphigh merujuk ini . Sepertinya sudah dihapus / dirombak di Jawa 8.
Cedric Reichenbach
148

(Aku tidak sepenuhnya senang dengan jawaban sejauh ini, jadi ini usahaku ...)

Saya pikir komentar Kevin Wright ( "Anda bisa membuat Janji dan itu terserah Anda untuk menyimpannya. Ketika orang lain membuat Anda berjanji, Anda harus menunggu untuk melihat apakah mereka menghormatinya di Masa Depan" ) merangkumnya dengan cukup baik, tetapi beberapa Penjelasan bisa bermanfaat.

Berjangka dan janji adalah konsep yang sangat mirip, perbedaannya adalah bahwa masa depan adalah wadah baca-saja untuk hasil yang belum ada, sementara janji dapat ditulis (biasanya hanya sekali). Java 8 CompletableFuture dan Guava SettableFuture dapat dianggap sebagai janji, karena nilainya dapat diatur ("selesai"), tetapi mereka juga mengimplementasikan antarmuka Masa Depan, oleh karena itu tidak ada perbedaan untuk klien.

Hasil masa depan akan ditetapkan oleh "orang lain" - oleh hasil perhitungan yang tidak sinkron. Perhatikan bagaimana FutureTask - masa depan klasik - harus diinisialisasi dengan Callable atau Runnable, tidak ada konstruktor tanpa argumen, dan FutureTask dan FutureTask keduanya hanya-baca dari luar (metode yang ditetapkan FutureTask dilindungi). Nilai akan ditetapkan ke hasil perhitungan dari dalam.

Di sisi lain, hasil dari janji dapat ditetapkan oleh "Anda" (atau bahkan oleh siapa saja) kapan saja karena memiliki metode setter publik. CompletableFuture dan SettableFuture dapat dibuat tanpa tugas apa pun, dan nilainya dapat diatur kapan saja. Anda mengirim janji ke kode klien, dan memenuhinya nanti seperti yang Anda inginkan.

Perhatikan bahwa CompletableFuture bukan janji "murni", ia dapat diinisialisasi dengan tugas seperti FutureTask, dan fitur yang paling berguna adalah rantai proses langkah yang tidak terkait.

Juga perhatikan bahwa janji tidak harus menjadi subtipe masa depan dan tidak harus menjadi objek yang sama. Dalam Scala, objek Masa Depan dibuat oleh perhitungan asinkron atau oleh objek Janji yang berbeda . Dalam C ++ situasinya mirip: objek janji digunakan oleh produsen dan objek masa depan oleh konsumen. Keuntungan dari pemisahan ini adalah bahwa klien tidak dapat menetapkan nilai masa depan.

Baik Spring dan EJB 3.1 memiliki kelas AsyncResult, yang mirip dengan janji Scala / C ++. AsyncResult tidak mengimplementasikan Future tetapi ini bukan masa depan yang sebenarnya: metode asynchronous di Spring / EJB mengembalikan objek Future yang berbeda dan hanya-baca melalui beberapa sihir latar belakang, dan masa depan "nyata" kedua ini dapat digunakan oleh klien untuk mengakses hasilnya.

lbalazscs
sumber
116

Saya sadar bahwa sudah ada jawaban yang diterima tetapi ingin menambahkan dua sen saya:

TLDR: Masa Depan dan Janji adalah dua sisi dari operasi asinkron: konsumen / pemanggil vs. produsen / implementor .

Sebagai pemanggil metode API asinkron, Anda akan mendapatkan Futuresebagai pegangan untuk hasil perhitungan. Anda dapat misalnya memanggilnya get()untuk menunggu perhitungannya selesai dan mengambil hasilnya.

Sekarang pikirkan bagaimana metode API ini benar-benar diimplementasikan: Implementor harus Futuresegera mengembalikan . Mereka bertanggung jawab untuk menyelesaikan masa depan itu segera setelah perhitungan dilakukan (yang akan mereka ketahui karena menerapkan logika pengiriman ;-)). Mereka akan menggunakan a Promise/ CompletableFutureuntuk melakukan hal itu: Bangun dan kembalikan CompletableFuturesegera, dan panggil complete(T result)setelah perhitungan selesai.

Rahel Lththy
sumber
1
Apakah ini menyiratkan bahwa Janji selalu merupakan subkelas Masa Depan, dan bahwa kemampuan menulis Masa Depan hanya dikaburkan oleh jenisnya?
devios1
Saya tidak berpikir itu tersirat . Dari sisi implementasi, sering kali demikian (misalnya di Jawa, Scala).
Rahel Lüthy
74

Saya akan memberikan contoh tentang apa itu Janji dan bagaimana nilainya dapat ditetapkan kapan saja, berlawanan dengan Masa Depan, yang nilainya hanya dapat dibaca.

Misalkan Anda memiliki seorang ibu dan Anda meminta uang kepadanya.

// Now , you trick your mom into creating you a promise of eventual
// donation, she gives you that promise object, but she is not really
// in rush to fulfill it yet:
Supplier<Integer> momsPurse = ()-> {

        try {
            Thread.sleep(1000);//mom is busy
        } catch (InterruptedException e) {
            ;
        }

        return 100;

    };


ExecutorService ex = Executors.newFixedThreadPool(10);

CompletableFuture<Integer> promise =  
CompletableFuture.supplyAsync(momsPurse, ex);

// You are happy, you run to thank you your mom:
promise.thenAccept(u->System.out.println("Thank you mom for $" + u ));

// But your father interferes and generally aborts mom's plans and 
// completes the promise (sets its value!) with far lesser contribution,
// as fathers do, very resolutely, while mom is slowly opening her purse 
// (remember the Thread.sleep(...)) :
promise.complete(10); 

Output dari itu adalah:

Thank you mom for $10

Janji Mom telah dibuat, tetapi menunggu beberapa acara "penyelesaian".

CompletableFuture<Integer> promise...

Anda membuat acara seperti itu, menerima janjinya dan mengumumkan rencana Anda untuk berterima kasih kepada ibumu:

promise.thenAccept...

Pada saat ini ibu mulai membuka dompetnya ... tetapi sangat lambat ...

dan ayah mencampuri lebih cepat dan menyelesaikan janjinya daripada ibumu:

promise.complete(10);

Pernahkah Anda memperhatikan seorang eksekutor yang saya tulis secara eksplisit?

Menariknya, jika Anda menggunakan pelaksana implisit default sebagai gantinya (commonPool) dan ayah tidak ada di rumah, tetapi hanya ibu yang memiliki "dompet lambat", maka janjinya hanya akan selesai, jika program ini hidup lebih lama dari yang dibutuhkan ibu untuk mendapatkan uang dari tas.

Eksekutor default bertindak seperti "daemon" dan tidak menunggu semua janji dipenuhi. Saya belum menemukan deskripsi yang baik tentang fakta ini ...

Vladimir Nabokov
sumber
8
Sangat menyenangkan membaca yang ini! Saya tidak berpikir saya bisa melupakan masa depan dan berjanji lagi.
user1532146
2
Ini harus diterima sebagai jawabannya. Ini seperti membaca sebuah cerita. Terima kasih @Vladimir
Phillen
Terima kasih @Vladimir
intvprep
9

Tidak yakin apakah ini bisa menjadi jawaban tetapi ketika saya melihat apa yang dikatakan orang lain untuk seseorang, mungkin Anda membutuhkan dua abstraksi terpisah untuk kedua konsep ini sehingga salah satunya ( Future) hanya merupakan tampilan hanya baca dari yang lain ( Promise) ... tapi sebenarnya ini tidak diperlukan.

Sebagai contoh, lihat bagaimana janji-janji didefinisikan dalam javascript:

https://promisesaplus.com/

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Fokusnya adalah pada kompabilitas menggunakan thenmetode seperti:

asyncOp1()
.then(function(op1Result){
  // do something
  return asyncOp2();
})
.then(function(op2Result){
  // do something more
  return asyncOp3();
})
.then(function(op3Result){
  // do something even more
  return syncOp4(op3Result);
})
...
.then(function(result){
  console.log(result);
})
.catch(function(error){
  console.log(error);
})

yang membuat perhitungan asinkron terlihat seperti sinkron:

try {
  op1Result = syncOp1();
  // do something
  op1Result = syncOp2();
  // do something more
  op3Result = syncOp3();
  // do something even more
  syncOp4(op3Result);
  ...
  console.log(result);
} catch(error) {
  console.log(error);
}

yang cukup keren. (Tidak sekeren async-tunggu tapi async-tunggu hanya menghilangkan boilerplate .... lalu (fungsi (hasil) {.... darinya).

Dan sebenarnya abstraksi mereka cukup bagus sebagai konstruktor janji

new Promise( function(resolve, reject) { /* do it */ } );

memungkinkan Anda untuk memberikan dua panggilan balik yang dapat digunakan untuk menyelesaikan dengan Promisesukses atau dengan kesalahan. Sehingga hanya kode yang membuat Promisebisa menyelesaikannya dan kode yang menerima objek yang sudah dibangun Promisememiliki tampilan hanya baca.

Dengan warisan hal di atas dapat dicapai jika tekad dan penolakan adalah metode yang dilindungi.

bodrin
sumber
4
+1. Ini adalah jawaban yang benar untuk pertanyaan ini. CompletableFuturemungkin memiliki beberapa kesamaan dengan Promisetetapi itu tetap bukanPromise , karena cara itu dimaksudkan untuk dikonsumsi berbeda: Promisehasil a dikonsumsi oleh panggilan then(function), dan fungsi dieksekusi dalam konteks produsen segera setelah produsen memanggil resolve. Hasil A Futuredikonsumsi dengan menelepon getyang menyebabkan utas konsumen menunggu sampai utas produsen menghasilkan nilai, lalu memprosesnya di konsumen. Futuresecara inheren multithreaded, tapi ...
Periata Breatta
5
... sangat mungkin untuk menggunakan Promisehanya dengan satu utas (dan pada kenyataannya itulah lingkungan yang sebenarnya mereka dirancang untuk: aplikasi javascript umumnya hanya memiliki satu utas, sehingga Anda tidak dapat menerapkannya di Futuresana). PromiseOleh karena itu jauh lebih ringan dan efisien daripada Future, tetapi Futuredapat membantu dalam situasi yang lebih kompleks dan membutuhkan kerjasama antara benang yang tidak dapat dengan mudah diatur dengan menggunakan Promises. Untuk meringkas: Promiseadalah model dorong, sementara Futureadalah model tarikan (lih Iterable vs Observable)
Periata Breatta
@PeriataBreatta Bahkan dalam lingkungan single-threaded, harus ada sesuatu yang memenuhi janji (yang biasanya berjalan seperti thread yang berbeda, misalnya, a XMLHttpRequest). Saya tidak percaya klaim efisiensi, apakah Anda memiliki beberapa angka? +++ Konon, penjelasan yang sangat bagus.
maaartinus
1
@maaartinus - ya, sesuatu harus memenuhi janji, tapi itu bisa (dan dalam banyak kasus memang) dilakukan dengan menggunakan loop tingkat atas yang melakukan polling untuk perubahan dalam kondisi eksternal dan menyelesaikan janji mana pun yang terkait dengan tindakan yang telah selesai. Dari segi efisiensi, saya tidak memiliki angka pasti untuk Janji, tetapi perhatikan bahwa memanggil getyang belum terselesaikan Futuretentu akan melibatkan 2 sakelar konteks utas, yang setidaknya beberapa tahun lalu kemungkinan membutuhkan sekitar 50 kita .
Periata Breatta
@PeriataBreatta Sebenarnya komentar Anda harus menjadi solusi yang diterima. Saya mencari penjelasan (pull / push, single / multi-thread) seperti milik Anda.
Thomas Jacob
5

Untuk kode klien, Janji adalah untuk mengamati atau melampirkan panggilan balik ketika suatu hasil tersedia, sedangkan Masa Depan adalah untuk menunggu hasil dan kemudian melanjutkan. Secara teoritis apa pun yang mungkin dilakukan dengan masa depan apa yang dapat dilakukan dengan janji, tetapi karena perbedaan gaya, API yang dihasilkan untuk janji dalam berbagai bahasa membuat rantai lebih mudah.

pengguna2562234
sumber
2

Tidak ada metode yang ditetapkan di antarmuka Masa Depan, hanya dapatkan metode, jadi ini hanya baca. Tentang CompletableFuture, artikel ini mungkin membantu. masa depan yang dapat diselesaikan

Jacky
sumber