Sự khác biệt giữa Future
và là Promise
gì?
Cả hai đều hành động như một người giữ chỗ cho kết quả trong tương lai, nhưng sự khác biệt chính ở đâu?
Sự khác biệt giữa Future
và là Promise
gì?
Cả hai đều hành động như một người giữ chỗ cho kết quả trong tương lai, nhưng sự khác biệt chính ở đâu?
Câu trả lời:
Theo cuộc thảo luận này , Promise
cuối cùng đã được kêu gọi CompletableFuture
đưa vào Java 8 và javadoc của nó giải thích:
Tương lai có thể được hoàn thành rõ ràng (đặt giá trị và trạng thái của nó) và có thể được sử dụng làm CompleteionStage, hỗ trợ các chức năng và hành động phụ thuộc kích hoạt khi hoàn thành.
Một ví dụ cũng được đưa ra trong danh sách:
f.then((s -> aStringFunction(s)).thenAsync(s -> ...);
Lưu ý rằng API cuối cùng hơi khác một chút nhưng cho phép thực thi không đồng bộ tương tự:
CompletableFuture<String> f = ...;
f.thenApply(this::modifyString).thenAccept(System.out::println);
(Tôi không hoàn toàn hài lòng với câu trả lời cho đến nay, vì vậy đây là nỗ lực của tôi ...)
Tôi nghĩ rằng nhận xét của Kevin Wright ( "Bạn có thể thực hiện Lời hứa và tùy thuộc vào bạn để giữ lời hứa. Khi người khác hứa với bạn, bạn phải chờ xem họ có tôn trọng điều đó trong tương lai không" , nhưng một số giải thích có thể hữu ích.
Tương lai và lời hứa là những khái niệm khá giống nhau, sự khác biệt là tương lai là một thùng chứa chỉ đọc cho một kết quả chưa tồn tại, trong khi một lời hứa có thể được viết (thường chỉ một lần). Java 8 CompleteableFuture và Guava SfortFuture có thể được coi là lời hứa, vì giá trị của chúng có thể được đặt ("hoàn thành"), nhưng chúng cũng thực hiện giao diện Tương lai, do đó không có sự khác biệt nào cho máy khách.
Kết quả của tương lai sẽ được đặt bởi "người khác" - bằng kết quả của một tính toán không đồng bộ. Lưu ý cách FutureTask - một tương lai cổ điển - phải được khởi tạo với Callable hoặc Runnable, không có hàm tạo đối số và cả Future và FutureTask đều chỉ đọc từ bên ngoài (các phương thức thiết lập của FutureTask được bảo vệ). Giá trị sẽ được đặt thành kết quả tính toán từ bên trong.
Mặt khác, kết quả của một lời hứa có thể được đặt ra bởi "bạn" (hoặc trên thực tế bởi bất kỳ ai) bất cứ lúc nào vì nó có phương thức thiết lập công khai. Cả CompleteableFuture và SfortFuture đều có thể được tạo mà không cần bất kỳ tác vụ nào và giá trị của chúng có thể được đặt bất cứ lúc nào. Bạn gửi lời hứa đến mã máy khách và thực hiện nó sau này nếu muốn.
Lưu ý rằng CompleteableFuture không phải là một lời hứa "thuần túy", nó có thể được khởi tạo với một tác vụ giống như FutureTask và tính năng hữu ích nhất của nó là chuỗi các bước xử lý không liên quan.
Cũng lưu ý rằng một lời hứa không phải là một kiểu con của tương lai và nó không phải là cùng một đối tượng. Trong Scala, một đối tượng Tương lai được tạo bởi một tính toán không đồng bộ hoặc bởi một đối tượng Promise khác . Trong C ++, tình huống tương tự: đối tượng lời hứa được sử dụng bởi nhà sản xuất và đối tượng tương lai của người tiêu dùng. Ưu điểm của sự tách biệt này là máy khách không thể đặt giá trị của tương lai.
Cả Spring và EJB 3.1 đều có lớp AsyncResult, tương tự như các lời hứa của Scala / C ++. AsyncResult thực hiện Tương lai nhưng đây không phải là tương lai thực: các phương thức không đồng bộ trong Spring / EJB trả về một đối tượng Tương lai chỉ đọc khác thông qua một số phép thuật nền và khách hàng có thể sử dụng tương lai "thực" thứ hai này để truy cập kết quả.
Tôi biết rằng đã có một câu trả lời được chấp nhận nhưng vẫn muốn thêm hai xu của tôi:
TLDR: Tương lai và Hứa hẹn là hai mặt của hoạt động không đồng bộ: người tiêu dùng / người gọi so với nhà sản xuất / người thực hiện .
Là người gọi phương thức API không đồng bộ, bạn sẽ nhận được Future
phần xử lý kết quả tính toán. Ví dụ, bạn có thể gọi get()
nó để chờ tính toán hoàn thành và lấy kết quả.
Bây giờ hãy nghĩ về cách phương thức API này thực sự được triển khai: Người triển khai phải trả lại Future
ngay lập tức. Họ chịu trách nhiệm hoàn thành tương lai đó ngay khi tính toán được thực hiện (điều mà họ sẽ biết vì nó đang thực hiện logic công văn ;-)). Họ sẽ sử dụng a Promise
/ CompletableFuture
để làm việc đó: Xây dựng và trả lại CompletableFuture
ngay lập tức và gọi complete(T result)
sau khi tính toán xong.
Tôi sẽ đưa ra một ví dụ về Promise là gì và làm thế nào giá trị của nó có thể được đặt bất cứ lúc nào, ngược lại với Tương lai, giá trị nào chỉ có thể đọc được.
Giả sử bạn có một người mẹ và bạn xin tiền cô ấy.
// 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);
Đầu ra của nó là:
Thank you mom for $10
Lời hứa của mẹ đã được tạo ra, nhưng chờ đợi một sự kiện "hoàn thành".
CompletableFuture<Integer> promise...
Bạn đã tạo ra sự kiện như vậy, chấp nhận lời hứa của cô ấy và thông báo kế hoạch của bạn để cảm ơn mẹ của bạn:
promise.thenAccept...
Lúc này mẹ bắt đầu mở ví ... nhưng rất chậm ...
và cha đã can thiệp nhanh hơn nhiều và hoàn thành lời hứa thay cho mẹ bạn:
promise.complete(10);
Bạn có nhận thấy một giám đốc điều hành mà tôi đã viết rõ ràng?
Thật thú vị, nếu bạn sử dụng một giám đốc điều hành ngầm định mặc định (commonPool) và cha không ở nhà, nhưng chỉ có mẹ với "ví chậm", thì lời hứa của cô sẽ chỉ hoàn thành, nếu chương trình sống lâu hơn mẹ cần lấy tiền từ cái ví.
Người thực thi mặc định hành động giống như một "daemon" và không chờ đợi tất cả các lời hứa được thực hiện. Tôi đã không tìm thấy một mô tả tốt về thực tế này ...
Không chắc đây có phải là một câu trả lời không nhưng khi tôi thấy những gì người khác đã nói cho ai đó thì có vẻ như bạn cần hai khái niệm trừu tượng riêng biệt cho cả hai khái niệm này để một trong số chúng ( Future
) chỉ là một cái nhìn chỉ đọc về cái kia ( Promise
) ... nhưng thực sự điều này là không cần thiết.
Ví dụ: hãy xem cách các lời hứa được xác định trong javascript:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Trọng tâm là khả năng kết hợp bằng then
phương pháp như:
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);
})
mà làm cho tính toán không đồng bộ trông giống như đồng bộ:
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);
}
đó là khá mát mẻ. (Không tuyệt vời như async-await nhưng async-await chỉ cần loại bỏ phần soạn sẵn .... sau đó (hàm (kết quả) {.... khỏi nó).
Và thực sự sự trừu tượng của họ là khá tốt như người xây dựng lời hứa
new Promise( function(resolve, reject) { /* do it */ } );
cho phép bạn cung cấp hai cuộc gọi lại có thể được sử dụng để hoàn Promise
thành thành công hoặc có lỗi. Vì vậy, chỉ có mã xây dựng Promise
có thể hoàn thành nó và mã nhận được một Promise
đối tượng đã được xây dựng có chế độ xem chỉ đọc.
Với sự kế thừa, những điều trên có thể đạt được nếu giải quyết và từ chối là các phương thức được bảo vệ.
CompletableFuture
có thể có một số điểm tương đồng với một Promise
nhưng nó vẫn không phải là mộtPromise
, bởi vì cách nó được tiêu thụ là khác nhau: Promise
kết quả được tiêu thụ bằng cách gọi then(function)
và chức năng được thực thi trong bối cảnh của nhà sản xuất ngay sau khi nhà sản xuất gọi resolve
. Future
Kết quả của A được tiêu thụ bằng cách gọi get
khiến cho chuỗi tiêu dùng chờ cho đến khi luồng sản xuất tạo ra giá trị, sau đó xử lý nó trong tiêu dùng. Future
vốn đã đa chiều, nhưng ...
Promise
chỉ với một luồng duy nhất (và trên thực tế đó là môi trường chính xác mà chúng được thiết kế ban đầu: các ứng dụng javascript thường chỉ có một luồng duy nhất, vì vậy bạn không thể thực hiện Future
ở đó). Promise
do đó là nhiều hơn trọng lượng nhẹ và hiệu quả hơn Future
, nhưng Future
có thể hữu ích trong các tình huống phức tạp hơn và đòi hỏi sự hợp tác giữa các chủ đề mà không thể dễ dàng được sắp xếp bằng cách sử dụng Promise
s. Tóm lại: Promise
là mô hình đẩy, trong khi Future
là mô hình kéo (cf Iterable vs Observable)
XMLHttpRequest
). Tôi không tin vào tuyên bố hiệu quả, bạn có tình cờ có một số số liệu? +++ Điều đó nói rằng, một lời giải thích rất tốt đẹp.
get
không được giải quyết Future
sẽ nhất thiết phải có 2 công tắc ngữ cảnh luồng, mà ít nhất vài năm trở lại đây có thể sẽ cần khoảng 50 chúng tôi .
Đối với mã máy khách, Promise là để quan sát hoặc đính kèm gọi lại khi có kết quả, trong khi Tương lai là chờ kết quả và sau đó tiếp tục. Về mặt lý thuyết bất cứ điều gì có thể làm với tương lai những gì có thể thực hiện với các lời hứa, nhưng do sự khác biệt về kiểu dáng, API kết quả cho các lời hứa trong các ngôn ngữ khác nhau giúp việc xâu chuỗi dễ dàng hơn.
Không có phương thức thiết lập nào trong giao diện Tương lai, chỉ có phương thức lấy, vì vậy nó chỉ đọc. Về CompleteableFuture, bài viết này có thể hữu ích. hoàn thành
Promise
và tùy thuộc vào bạn để giữ nó. Khi người khác hứa với bạn, bạn phải chờ xem họ có tôn trọng điều đó trongFuture