Sự khác biệt giữa Trì hoãn, Hứa hẹn và Tương lai trong JavaScript là gì?


300

Sự khác biệt giữa Trì hoãn, Hứa hẹn và Tương lai là gì?
Có một lý thuyết thường được phê duyệt đằng sau cả ba?


11
Tôi không nghĩ điều này có liên quan đến jQuery ...
BoltClock


8
Tôi đã không sử dụng chúng cho mình nhưng đây là một đoạn giới thiệu khá hay trên wikipedia en.wikipedia.org/wiki/Futures_and_promises . Mặc dù tôi không hiểu đầy đủ về trường hợp sử dụng. Trong một sự kiện async điều khiển ngôn ngữ như javascript. Thoạt nhìn tôi không thể thấy những gì họ cung cấp qua các cuộc gọi lại, ngoài việc có thể là một api sạch hơn. Tôi rất thích nếu ai đó có thể cung cấp một trường hợp sử dụng ví dụ và cho thấy các khái niệm này được áp dụng như thế nào và tại sao các cuộc gọi lại sẽ là một giải pháp không hiệu quả. @duri điều này không liên quan gì đến jQuery. Thẻ jQuery có thể được gỡ bỏ không
AshHeskes

2
@ jfriend00 liên kết tuyệt vời, có lẽ nên được trả lời.
fncomp

@ jfriend00 liên kết mới - msdn.microsoft.com/en-us/magazine/gg723713.aspx
c69

Câu trả lời:


97

Trước sự không thích rõ ràng về cách tôi đã cố gắng trả lời câu hỏi của OP. Câu trả lời theo nghĩa đen là, một lời hứa là một cái gì đó được chia sẻ với các đối tượng khác, trong khi hoãn lại nên được giữ kín. Chủ yếu, một lời nói bị trì hoãn (thường kéo dài Promise) có thể tự giải quyết, trong khi một lời hứa có thể không thể thực hiện được.

Nếu bạn quan tâm đến các chi tiết vụn vặt, hãy xem Promise / A + .


Theo như tôi biết, mục đích bao trùm là cải thiện sự rõ ràng và nới lỏng khớp nối thông qua một giao diện được tiêu chuẩn hóa. Xem gợi ý đọc từ @ jfriend00:

Thay vì trực tiếp chuyển các cuộc gọi lại đến các chức năng, một cái gì đó có thể dẫn đến các giao diện được kết nối chặt chẽ, sử dụng các lời hứa cho phép người ta tách các mối quan tâm về mã đồng bộ hoặc không đồng bộ.

Cá nhân, tôi thấy việc trì hoãn đặc biệt hữu ích khi xử lý các ví dụ được tạo bởi các yêu cầu không đồng bộ, tải tập lệnh có mạng phụ thuộc và cung cấp phản hồi của người dùng để tạo dữ liệu theo cách không chặn.

Thật vậy, hãy so sánh hình thức gọi lại thuần túy để làm một cái gì đó sau khi tải CodeMirror ở chế độ JS không đồng bộ (xin lỗi, tôi đã không sử dụng jQuery trong một thời gian ):

/* assume getScript has signature like: function (path, callback, context) 
   and listens to onload && onreadystatechange */
$(function () {
   getScript('path/to/CodeMirror', getJSMode);

   // onreadystate is not reliable for callback args.
   function getJSMode() {
       getScript('path/to/CodeMirror/mode/javascript/javascript.js', 
           ourAwesomeScript);
   };

   function ourAwesomeScript() {
       console.log("CodeMirror is awesome, but I'm too impatient.");
   };
});

Đối với phiên bản được hứa hẹn (một lần nữa, xin lỗi, tôi không cập nhật trên jQuery):

/* Assume getScript returns a promise object */
$(function () {
   $.when(
       getScript('path/to/CodeMirror'),
       getScript('path/to/CodeMirror/mode/javascript/javascript.js')
   ).then(function () {
       console.log("CodeMirror is awesome, but I'm too impatient.");
   });
});

Xin lỗi về mã bán giả, nhưng tôi hy vọng nó làm cho ý tưởng cốt lõi phần nào rõ ràng. Về cơ bản, bằng cách trả lại một lời hứa được tiêu chuẩn hóa, bạn có thể vượt qua lời hứa xung quanh, do đó cho phép phân nhóm rõ ràng hơn.


10
Mặc dù câu trả lời này có thể hữu ích, nhưng thực tế nó không giải quyết được câu hỏi: cái gọi là trì hoãn là tương lai hoặc lời hứa, tùy thuộc vào việc thực hiện.
awdz9nld

@ MartinKällman Bạn nói đúng! Tôi đã không xem lại điều này trong một thời gian và đã học được một chút. Tôi sẽ đăng một câu trả lời riêng bên dưới, nhưng hãy để lại câu này vì mọi người dường như đã được hưởng lợi từ ví dụ sử dụng.
fncomp

@ MartinKällman, xem xét viết một câu trả lời mới. Tuy nhiên, tôi nghĩ OP thực sự muốn biết Promise và Trì hoãn là để làm gì. Câu trả lời cho câu hỏi thực tế của anh ta là, "đại khái là" trì hoãn có thể tự giải quyết. AFAIK, lý thuyết đằng sau những lời hứa và trì hoãn đến từ [Lập trình phản ứng chức năng | haskell.org/haskellwiki/Feftal_Reactive_Programming] , đó là một kỹ thuật để gọi lại . "
fncomp

2
Điều này là hoàn toàn sai và các ví dụ của bạn cũng dễ thực hiện với các cuộc gọi lại. Hứa hẹn không phải là về tổng hợp gọi lại và tách rời mà cung cấp DSL để viết mã không đồng bộ như mã đồng bộ được viết. Đặc biệt fn(callback, errback)là không có bất kỳ kết hợp chặt chẽ hoặc ít hữu ích hơn fn().then(callback, errback)- nhưng dù sao đó là một cách sai để sử dụng lời hứa. Tôi đặc biệt ghét $.whenví dụ sùng bái hàng hóa - hoàn toàn không có lý do gì bạn không thể có $.whenchức năng làm việc với các cuộc gọi lại.
Esailija

Điều này không trả lời cho câu hỏi mặc dù +1 mà tôi có thể biết địa ngục gọi lại là gì.
Bhojendra Rauniyar

146

Những câu trả lời này, bao gồm câu trả lời được chọn, rất tốt để đưa ra những lời hứa về mặt khái niệm, nhưng thiếu chi tiết cụ thể về sự khác biệt chính xác trong thuật ngữ phát sinh khi sử dụng các thư viện thực hiện chúng (và có những khác biệt quan trọng).

Vì nó vẫn là một thông số kỹ thuật đang phát triển , câu trả lời hiện tại đến từ việc cố gắng khảo sát cả các tài liệu tham khảo (như wikipedia ) và triển khai (như jQuery ):

  • Trì hoãn : Không bao giờ được mô tả trong các tài liệu tham khảo phổ biến, 1 2 3 4 nhưng thường được sử dụng bởi các triển khai như là trọng tài giải quyết lời hứa (thực hiện và ). 5 6 7 resolvereject

    Đôi khi, việc trì hoãn cũng là những lời hứa (thực hiện then), 5 6 lần khác, nó được coi là thuần túy hơn khi chỉ có khả năng giải quyết và buộc người dùng truy cập vào lời hứa để sử dụng . 7 then

  • Promise : Từ bao quát nhất cho chiến lược đang thảo luận.

    Một đối tượng proxy lưu trữ kết quả của một hàm mục tiêu có tính đồng bộ mà chúng tôi muốn trừu tượng hóa, cộng với việc phơi bày một thenhàm chấp nhận một hàm mục tiêu khác và trả lại một lời hứa mới. 2

    Ví dụ từ CommonJS :

    > asyncComputeTheAnswerToEverything()
        .then(addTwo)
        .then(printResult);
    44

     

    Luôn được mô tả trong các tài liệu tham khảo phổ biến, mặc dù không bao giờ quy định trách nhiệm giải quyết trách nhiệm của ai. 1 2 3 4

    Luôn luôn có mặt trong các triển khai phổ biến, và không bao giờ đưa ra khả năng giải quyết. 5 6 7

  • Tương lai : một thuật ngữ dường như không được dùng trong một số tài liệu tham khảo phổ biến 1 và ít nhất một triển khai phổ biến, 8 nhưng dường như bị loại bỏ khỏi cuộc thảo luận để ưu tiên cho thuật ngữ 'lời hứa' 3 và không phải lúc nào cũng được đề cập trong phần giới thiệu phổ biến cho chủ đề này. 9

    Tuy nhiên, ít nhất một thư viện sử dụng thuật ngữ chung để trừu tượng hóa tính đồng bộ và xử lý lỗi, trong khi không cung cấp thenchức năng. 10 Không rõ ràng nếu tránh thuật ngữ 'lời hứa' là có chủ ý, nhưng có lẽ là một lựa chọn tốt vì các lời hứa được xây dựng xung quanh 'các mục tiêu'. 2

Người giới thiệu

  1. Wikipedia về lời hứa và tương lai
  2. Lời hứa / A + spec
  3. Tiêu chuẩn DOM về lời hứa
  4. Tiêu chuẩn DOM Hứa Spec WIP
  5. Bộ công cụ DOJO hoãn lại
  6. Trì hoãn jQuery
  7. Q
  8. Tương lai
  9. Phần Javascript chức năng trên Promise
  10. Tương lai trong thử nghiệm tích hợp AngularJS

Linh tinh có khả năng gây nhầm lẫn


5
Để thêm một chút rõ ràng hơn về thuật ngữ "Tương lai" - tương lai có một lịch sử lâu dài trên nhiều ngôn ngữ lập trình có từ giữa những năm 80. Và thuật ngữ này vẫn còn được sử dụng rộng rãi ngày nay, đặc biệt là trên JVM. JavaScript dường như đã chọn sử dụng thuật ngữ "Promise" để có nghĩa tương tự như ý nghĩa của Java trong "Tương lai". Scala tách khái niệm tương tự thành một "Tương lai" và "Lời hứa" để chỉ xử lý "đọc" và xử lý "viết" về những gì các lập trình viên JavaScript gọi là Lời hứa.
Heather Miller

1
Và tất nhiên, Microsoft đã phải đưa ra thuật ngữ riêng của họ cho nó, vì vậy, trong C # họ được gọi làTask
BlueRaja - Danny Pflughoeft

72

Điều thực sự khiến tất cả nhấp vào tôi là bài thuyết trình này của Domenic Denicola.

Trong một ý chính , anh ấy đã đưa ra mô tả mà tôi thích nhất, nó rất súc tích:

Điểm hứa hẹn là cung cấp cho chúng tôi trở lại thành phần chức năng và lỗi bong bóng trong thế giới không đồng bộ.

Nói cách khác, lời hứa là một cách cho phép chúng ta viết mã không đồng bộ gần như dễ viết như thể nó là đồng bộ .

Xem xét ví dụ này, với những lời hứa:

getTweetsFor("domenic") // promise-returning async function
    .then(function (tweets) {
        var shortUrls = parseTweetsForUrls(tweets);
        var mostRecentShortUrl = shortUrls[0];
        return expandUrlUsingTwitterApi(mostRecentShortUrl); // promise-returning async function
    })
    .then(doHttpRequest) // promise-returning async function
    .then(
        function (responseBody) {
            console.log("Most recent link text:", responseBody);
        },
        function (error) {
            console.error("Error with the twitterverse:", error);
        }
    );

Nó hoạt động như thể bạn đang viết mã đồng bộ này:

try {
    var tweets = getTweetsFor("domenic"); // blocking
    var shortUrls = parseTweetsForUrls(tweets);
    var mostRecentShortUrl = shortUrls[0];
    var responseBody = doHttpRequest(expandUrlUsingTwitterApi(mostRecentShortUrl)); // blocking x 2
    console.log("Most recent link text:", responseBody);
} catch (error) {
    console.error("Error with the twitterverse: ", error);
}

(Nếu điều này vẫn có vẻ phức tạp, hãy xem bản trình bày đó!)

Về Trì hoãn, đó là một cách để .resolve()hoặc .reject()lời hứa. Trong thông số Promising / B , nó được gọi .defer(). Trong jQuery, nó $.Deferred().

Xin lưu ý rằng, theo như tôi biết, việc triển khai Promise trong jQuery bị hỏng (xem ý chính đó), ít nhất là với jQuery 1.8.2.
Nó được cho là thực hiện Promise / A thenables , nhưng bạn không nhận được cách xử lý lỗi chính xác, theo nghĩa là toàn bộ chức năng "async try / Catch" sẽ không hoạt động. Thật đáng tiếc, bởi vì việc "thử / bắt" với mã async hoàn toàn tuyệt vời.

Nếu bạn định sử dụng Promise (bạn nên dùng thử với mã của riêng bạn!), Hãy sử dụng Q của Kris Kowal . Phiên bản jQuery chỉ là một số trình tổng hợp gọi lại để viết mã jQuery sạch hơn, nhưng bỏ lỡ điểm.

Về Tương lai, tôi không biết, tôi chưa thấy điều đó trong bất kỳ API nào.

Chỉnh sửa: Cuộc trò chuyện trên youtube của Domenic Denicola về Lời hứa từ bình luận của @Farm bên dưới.

Một trích dẫn của Michael Jackson (vâng, Michael Jackson ) từ video:

Tôi muốn bạn ghi cụm từ này trong tâm trí của bạn: Một lời hứa là một giá trị không đồng bộ .

Đây là một mô tả xuất sắc: một lời hứa giống như một biến số từ tương lai - một tham chiếu hạng nhất đến một cái gì đó, tại một thời điểm nào đó, sẽ tồn tại (hoặc xảy ra).


5
Một lời giải thích tuyệt vời về Tương lai (hiện được triển khai trong DOM!) Bởi một thành viên của nhóm cốt lõi W3 và Chrome sẽ được tìm thấy ở đây: xanthir.com/b4PY0
oligofren

1
@oligofren Cảm ơn liên kết, có vẻ tốt! Nhân tiện, thật là một favicon lol khó chịu bí ẩn.
Camilo Martin

1
Câu trả lời này cần rất nhiều upvote. Nó nên được bình chọn cao hơn câu trả lời được chấp nhận IMO.
Chev

1
Cuộc trò chuyện trên youtube của Domenic Denicola trên Promise: youtube.com/watch?v=hf1T_AONQJU
Nông trại

@Farm Tuyệt vời! Tôi sẽ thêm nó vào câu trả lời.
Camilo Martin

32

Một Promise đại diện cho một proxy cho một giá trị không nhất thiết phải biết đến khi sự hứa hẹn được tạo ra. Nó cho phép bạn liên kết các trình xử lý với giá trị thành công cuối cùng của hành động không đồng bộ hoặc lý do thất bại. Điều này cho phép các phương thức không đồng bộ trả về các giá trị như các phương thức đồng bộ: thay vì giá trị cuối cùng, phương thức không đồng bộ trả về một lời hứa sẽ có giá trị tại một thời điểm nào đó trong tương lai.

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

Các deferred.promise()phương pháp cho phép một chức năng không đồng bộ để ngăn chặn mã khác can thiệp vào tiến độ hoặc trạng thái của yêu cầu nội bộ của mình. Promise chỉ hiển thị các phương thức Trì hoãn cần thiết để đính kèm các trình xử lý bổ sung hoặc xác định trạng thái ( sau đó, thực hiện, thất bại, luôn luôn, đường ống, tiến trình, trạng thái và lời hứa ), nhưng không phải là các phương thức thay đổi trạng thái ( giải quyết, từ chối, thông báo, giải quyết, từ chối với, và thông báo với ).

Nếu mục tiêu được cung cấp, deferred.promise()sẽ đính kèm các phương thức vào nó và sau đó trả về đối tượng này thay vì tạo một đối tượng mới. Điều này có thể hữu ích để đính kèm hành vi Promise với một đối tượng đã tồn tại.

Nếu bạn đang tạo Trì hoãn, hãy giữ một tham chiếu đến Trì hoãn để có thể giải quyết hoặc từ chối tại một số điểm. Chỉ trả về đối tượng Promise thông qua deferred.promise () để mã khác có thể đăng ký gọi lại hoặc kiểm tra trạng thái hiện tại.

Đơn giản là chúng ta có thể nói rằng một Promise đại diện cho một giá trị chưa được biết đến như là một Trì hoãn đại diện cho công việc chưa hoàn thành.


nhập mô tả hình ảnh ở đây


1
cộng 1 cho biểu diễn biểu đồ. Bravisimo !! ^ _ ^
Ashok MA

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.