Sự khác biệt giữa Tương lai và Lời hứa là gì?


274

Sự khác biệt giữa Futurevà là Promisegì?
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?


99
Bạn có thể làm Promisevà 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
Kevin Wright


30
Một trong những bài viết Wikipedia ít hữu ích nhất tôi từng đọc
Fulluphigh

Câu trả lời:


145

Theo cuộc thảo luận này , Promisecuố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);

78
Đó không phải là lỗi của bạn Assylias, nhưng trích xuất javadoc cần một tác phẩm nghiêm túc của một tác giả công nghệ đàng hoàng. Trong lần đọc thứ năm của tôi, tôi có thể bắt đầu đánh giá cao những gì nó đang cố nói ... và tôi đi đến đây với sự hiểu biết về tương lai và những lời hứa đã có sẵn!
Củ cải đường-Củ cải đường

2
@ Beetroot-Củ cải đường dường như đã xảy ra cho đến bây giờ.
herman

1
@herman Cảm ơn - Tôi đã cập nhật liên kết để trỏ đến phiên bản cuối cùng của javadoc.
assylias

7
@ Beetroot-Củ cải đường Bạn nên kiểm tra tài liệu cho phương pháp Ngoại lệ. Nó sẽ làm một bài thơ tuyệt vời, nhưng là một thất bại đặc biệt của tài liệu có thể đọc được.
Fulluphigh

4
Đối với bất cứ ai tự hỏi, @Fulluphigh đang đề cập đến điều này . Có vẻ như nó đã bị xóa / đại tu trong Java 8.
Cedric Reichenbach

147

(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ả SpringEJB 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ả.


116

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 .

người gọi phương thức API không đồng bộ, bạn sẽ nhận được Futurephầ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 Futurengay 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 CompletableFuturengay lập tức và gọi complete(T result)sau khi tính toán xong.


1
Có phải điều này ngụ ý rằng một Lời hứa luôn là một lớp con của Tương lai, và khả năng có thể viết của Tương lai chỉ bị che khuất bởi loại hình?
devios1

Tôi không nghĩ rằng nó được ngụ ý . Thực hiện khôn ngoan, nó thường sẽ là trường hợp mặc dù (ví dụ trong Java, Scala).
Rahel Lüthy

74

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 ...


8
Thật thú vị khi đọc cái này! Tôi không nghĩ rằng tôi có thể quên tương lai và hứa hẹn nữa.
dùng1532146

2
Điều này phải được chấp nhận là câu trả lời. Nó giống như đọc một câu chuyện. Cảm ơn @Vladimir
Phillen

Cảm ơn @Vladimir
intvprep

9

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://promisesaplus.com/

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 thenphươ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 Promisethành thành công hoặc có lỗi. Vì vậy, chỉ có mã xây dựng Promisecó 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ếttừ chối là các phương thức được bảo vệ.


4
+1. Đây là câu trả lời chính xác cho câu hỏi này. CompletableFuturecó thể có một số điểm tương đồng với một Promisenhư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: Promisekế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. FutureKết quả của A được tiêu thụ bằng cách gọi getkhiế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. Futurevốn đã đa chiều, nhưng ...
Periata Breatta

5
... Hoàn toàn có thể sử dụng một Promisechỉ 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ở đó). Promisedo đó là nhiều hơn trọng lượng nhẹ và hiệu quả hơn Future, nhưng Futurecó 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 Promises. Tóm lại: Promiselà mô hình đẩy, trong khi Futurelà mô hình kéo (cf Iterable vs Observable)
Periata Breatta

@PeriataBreatta Ngay cả trong một môi trường đơn luồng, phải có một cái gì đó thực hiện lời hứa (thường là loại chạy như một luồng khác, ví dụ: một 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.
maaartinus

1
@maaartinus - vâng, một cái gì đó phải thực hiện lời hứa, nhưng nó có thể (và thực tế trong nhiều trường hợp là) được thực hiện bằng cách sử dụng vòng lặp cấp cao nhất để thăm dò các thay đổi ở trạng thái bên ngoài và giải quyết bất kỳ lời hứa nào liên quan đến các hành động đã kết thúc. Hiệu quả khôn ngoan, tôi không có số liệu chắc chắn về Promise cụ thể, nhưng lưu ý rằng việc kêu gọi getkhông được giải quyết Futuresẽ 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 .
Periata Breatta

@PeriataBreatta Thật ra bình luận của bạn phải là giải pháp được chấp nhận. Tôi đang tìm kiếm một lời giải thích (kéo / đẩy, đơn / đa luồng) như của bạn.
Thomas Jacob

5

Đố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.


2

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.