Khi nào .then (thành công, thất bại) được coi là một phản mẫu cho những lời hứa?


188

Tôi đã xem qua Câu hỏi thường gặp về lời hứa của bluebird , trong đó đề cập đến đó .then(success, fail)là một phản đề . Tôi hoàn toàn không hiểu lời giải thích của nó về việc thử và bắt. Điều gì sai với điều này sau đây?

some_promise_call()
.then(function(res) { logger.log(res) }, function(err) { logger.log(err) })

Có vẻ như ví dụ này gợi ý những điều sau đây là cách chính xác.

some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })

Có gì khác biệt?


1
then().catch()dễ đọc hơn, vì bạn không cần tìm kiếm dấu phẩy và điều tra là cuộc gọi lại này để thành công hay thất bại chi nhánh.
Krzysztof Safjanowski

7
@KevinB: Có nhiều sự khác biệt, hãy kiểm tra câu trả lời
Bergi

12
@KrzysztofSafjanowski - bị tàn phá bởi lập luận 'có vẻ tốt hơn'. Hoàn toàn sai!
Andrey Popov

6
LƯU Ý: Khi bạn đang sử dụng .catch, bạn không biết bước nào gây ra sự cố - bên trong chuỗi cuối cùng thenhoặc ở nơi nào khác trong chuỗi hứa hẹn. Vì vậy, nó có nhược điểm riêng của nó.
Vitaly-t

2
Tôi luôn thêm tên hàm vào các tham số .then () để làm cho nó dễ đọc, tức làsome_promise_call() .then(function fulfilled(res) { logger.log(res) }, function rejected(err) { logger.log(err) })
Shane Rowatt

Câu trả lời:


215

Có gì khác biệt?

Cuộc .then()gọi sẽ trả lại một lời hứa sẽ bị từ chối trong trường hợp cuộc gọi lại bị lỗi. Điều này có nghĩa là, khi thành công của bạn loggerthất bại, lỗi sẽ được chuyển sang cuộc .catch()gọi lại sau , nhưng không phải là failcuộc gọi lại đi cùng success.

Đây là sơ đồ luồng điều khiển :

sơ đồ điều khiển sau đó với hai đối số sơ đồ điều khiển của chuỗi bắt

Để thể hiện nó trong mã đồng bộ:

// some_promise_call().then(logger.log, logger.log)
then: {
    try {
        var results = some_call();
    } catch(e) {
        logger.log(e);
        break then;
    } // else
        logger.log(results);
}

Đối số thứ hai log(giống như đối số thứ nhất .then()) sẽ chỉ được thực thi trong trường hợp không có ngoại lệ xảy ra. Khối được gắn nhãn và breaktuyên bố cảm thấy hơi kỳ lạ, đây thực sự là những gì python đã try-except-elsecho (đọc khuyến nghị!).

// some_promise_call().then(logger.log).catch(logger.log)
try {
    var results = some_call();
    logger.log(results);
} catch(e) {
    logger.log(e);
}

Trình catchghi nhật ký cũng sẽ xử lý các trường hợp ngoại lệ từ cuộc gọi logger thành công.

Quá nhiều cho sự khác biệt.

Tôi không hiểu lời giải thích của nó về việc thử và bắt

Đối số là thông thường bạn muốn bắt lỗi trong mỗi bước xử lý và bạn không nên sử dụng nó trong chuỗi. Kỳ vọng là bạn chỉ có một trình xử lý cuối cùng xử lý tất cả các lỗi - trong khi, khi bạn sử dụng "antipotype", các lỗi trong một số cuộc gọi lại sau đó không được xử lý.

Tuy nhiên, mẫu này thực sự rất hữu ích: Khi bạn muốn xử lý các lỗi xảy ra trong chính xác bước này và bạn muốn làm một cái gì đó hoàn toàn khác khi không có lỗi xảy ra - tức là khi lỗi không thể phục hồi. Hãy lưu ý rằng điều này đang phân nhánh luồng kiểm soát của bạn. Tất nhiên, điều này đôi khi là mong muốn.


Điều gì sai với điều này sau đây?

some_promise_call()
.then(function(res) { logger.log(res) }, function(err) { logger.log(err) })

Rằng bạn phải lặp lại cuộc gọi lại. Bạn muốn

some_promise_call()
   .catch(function(e) {
       return e; // it's OK, we'll just log it
   })
   .done(function(res) {
       logger.log(res);
   });

Bạn cũng có thể xem xét sử dụng .finally()cho việc này.


7
đây là lời giải thích hữu ích nhất mà tôi đã đọc trong vài ngày (và tôi đã đọc rất nhiều). Tôi không thể giải thích tôi biết ơn như thế nào! :) Tôi nghĩ bạn nên nhấn mạnh thêm sự khác biệt giữa hai điều này, điều đó .catchsẽ bắt lỗi ngay cả bên trong chức năng thành công .. Cá nhân tôi thấy điều này cực kỳ sai khi bạn kết thúc với một điểm nhập lỗi, có thể nhận được nhiều lỗi từ nhiều hành động, nhưng đây là vấn đề của tôi. Dù sao - cảm ơn thông tin! Bạn không có một số công cụ giao tiếp trực tuyến mà bạn sẵn sàng chia sẻ để tôi có thể hỏi thêm vài điều nữa không? : P
Andrey Popov

2
Tôi hy vọng điều này sẽ cung cấp cho bạn một số upvote ở đây. Chắc chắn là một trong những lời giải thích tốt nhất của một Promisethợ máy quan trọng trên trang web này.
Patrick Roberts

2
.done()không phải là một phần của tiêu chuẩn, phải không? Ít nhất MDN không liệt kê phương pháp đó. Nó sẽ rất hữu ích.
ygoe

1
@ygoe Thật vậy. donelà một điều Bluebird về cơ bản không được chấp nhận bởi then+ phát hiện từ chối không xử lý.
Bergi

1
chỉ là một ghi chú từ bảng màu: các sơ đồ không có ý nghĩa gì :)
Benny K

37

Cả hai không hoàn toàn giống nhau. Sự khác biệt là ví dụ đầu tiên sẽ không bắt gặp một ngoại lệ được đưa vào successxử lý của bạn . Vì vậy, nếu phương thức của bạn chỉ nên trả lại các lời hứa đã được giải quyết, như thường lệ, bạn cần một catchtrình xử lý theo dõi (hoặc một phương thức khác thensuccesstham số trống ). Chắc chắn, có thể là thentrình xử lý của bạn không làm bất cứ điều gì có khả năng thất bại, trong trường hợp sử dụng một tham số 2 thencó thể tốt.

Nhưng tôi tin rằng điểm của văn bản bạn liên kết đến là thenhầu như hữu ích so với các cuộc gọi lại trong khả năng của nó để xâu chuỗi một loạt các bước không đồng bộ và khi bạn thực sự làm điều này, dạng 2 tham số của thentinh tế không hoạt động như mong đợi , vì lý do trên. Nó đặc biệt phản trực giác khi được sử dụng giữa chuỗi.

Là một người đã thực hiện rất nhiều công cụ không đồng bộ phức tạp và va vào các góc như thế này nhiều hơn tôi quan tâm, tôi thực sự khuyên bạn nên tránh mô hình chống này và đi theo phương pháp xử lý riêng.


18

Bằng cách xem xét các ưu điểm và nhược điểm của cả hai, chúng ta có thể đoán được tính toán phù hợp với tình huống. Đây là hai cách tiếp cận chính để thực hiện lời hứa. Cả hai đều có điểm cộng và điểm trừ

Cách tiếp cận bắt

some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })

Ưu điểm

  1. Tất cả các lỗi được xử lý bởi một khối bắt.
  2. Thậm chí bắt bất kỳ ngoại lệ trong khối sau đó.
  3. Chuỗi nhiều cuộc gọi lại thành công

Nhược điểm

  1. Trong trường hợp xâu chuỗi, việc hiển thị các thông báo lỗi khác nhau trở nên khó khăn.

Cách tiếp cận thành công / lỗi

some_promise_call()
.then(function success(res) { logger.log(res) },
      function error(err) { logger.log(err) })

Ưu điểm

  1. Bạn có được kiểm soát lỗi hạt mịn.
  2. Bạn có thể có chức năng xử lý lỗi phổ biến cho các loại lỗi khác nhau như lỗi db, lỗi 500, v.v.

Biến mất

  1. Bạn vẫn sẽ cần một cái khác catchnếu bạn muốn xử lý các lỗi được gọi bởi cuộc gọi lại thành công

Đối với ai đó cần gỡ lỗi các vấn đề sản xuất chỉ bằng một tệp nhật ký, tôi thích Cách tiếp cận thành công / lỗi hơn vì nó mang lại khả năng tạo chuỗi lỗi nguyên nhân có thể được ghi lại ở ranh giới thoát của ứng dụng.
Shane Rowatt 7/07/2016

câu hỏi giả sử tôi thực hiện cuộc gọi không đồng bộ, thực hiện một trong một số điều sau: 1) trả về thành công (mã trạng thái 2xx), 2) trả về không thành công (mã 4xx hoặc 5xx) nhưng không bị từ chối mỗi lần, 3) hoặc hoàn toàn không trả về ( kết nối internet bị hỏng). Đối với trường hợp # 1, cuộc gọi lại thành công trong .then được nhấn. Đối với trường hợp # 2, cuộc gọi lại lỗi trong .then được nhấn. Đối với trường hợp # 3, .catch được gọi. Đây là phân tích chính xác, phải không? Trường hợp # 2 là khó khăn nhất bc về mặt kỹ thuật, 4xx hoặc 5xx không phải là từ chối, nó vẫn trả về thành công. Vì vậy, chúng ta cần xử lý nó trong .then. ....Tôi hiểu thế có đúng không?
Benjamin Hoffman

"Đối với trường hợp # 2, cuộc gọi lại lỗi trong .then bị nhấn. Đối với trường hợp # 3, .catch được gọi. Đây là phân tích chính xác, phải không?" - Đó là cách tìm nạp hoạt động
aWebDeveloper

2

Giải thích đơn giản:

Trong ES2018

Khi phương thức bắt được gọi với đối số onRejection, các bước sau được thực hiện:

  1. Hãy để lời hứa là giá trị này.
  2. Trở về ? Gọi (lời hứa, "sau đó", «không xác định, bị từ chối»).

Điều đó có nghĩa là:

promise.then(f1).catch(f2)

bằng

promise.then(f1).then(undefiend, f2)

1

Việc sử dụng .then().catch()cho phép bạn kích hoạt Promise Chaining cần thiết để hoàn thành quy trình công việc. Bạn có thể cần phải đọc một số thông tin từ cơ sở dữ liệu sau đó bạn muốn chuyển nó sang API async sau đó bạn muốn thao tác phản hồi. Bạn có thể muốn đẩy phản hồi trở lại cơ sở dữ liệu. Xử lý tất cả các quy trình công việc này với khái niệm của bạn là có thể thực hiện được nhưng rất khó quản lý. Giải pháp tốt hơn sẽ là then().then().then().then().catch()nhận tất cả các lỗi chỉ trong một lần bắt và cho phép bạn giữ khả năng duy trì của mã.


0

Sử dụng then()catch()giúp chuỗi thành công và xử lý thất bại trên lời hứa. catch()làm việc trên lời hứa được trả lại bởi then(). Nó xử lý,

  1. Nếu lời hứa bị từ chối. Xem số 3 trong hình
  2. Nếu xảy ra lỗi trong trình xử lý thành công của then (), giữa các số dòng 4 đến 7 dưới đây. Xem # 2.a trong hình (Lỗi gọi lại then()không xử lý việc này.)
  3. Nếu xảy ra lỗi trong trình xử lý lỗi của (), dòng số 8 bên dưới. Xem # 3.b trong hình.

1. let promiseRef: Promise = this. aTimetakingTask (false); 2. promiseRef 3. .then( 4. (result) => { 5. /* successfully, resolved promise. 6. Work on data here */ 7. }, 8. (error) => console.log(error) 9. ) 10. .catch( (e) => { 11. /* successfully, resolved promise. 12. Work on data here */ 13. });

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

Lưu ý : Nhiều lần, trình xử lý lỗi có thể không được xác định nếu đã catch()được viết. EDIT: reject()kết quả trong cách gọi catch()chỉ khi xử lý lỗi trong then()không xác định. Thông báo số 3 trong hình tới catch(). Nó được gọi khi trình xử lý trong dòng # 8 và 9 không được xác định.

Điều này có ý nghĩa bởi vì lời hứa được trả lại bởi then()không có lỗi nếu một cuộc gọi lại chăm sóc nó.


Mũi tên từ số 3 đến cuộc catchgọi lại có vẻ sai.
Bergi

Cảm ơn! Với một cuộc gọi lại lỗi được xác định trong then (), nó không được gọi (dòng # 8 và # 9 trong đoạn mã). # 3 gọi một trong hai mũi tên. Điều này có ý nghĩa bởi vì lời hứa được trả lại sau đó () không có lỗi nếu một cuộc gọi lại đang chăm sóc nó. Chỉnh sửa câu trả lời!
VenCKi

-1

Thay vì lời nói, ví dụ tốt. Mã sau (nếu lời hứa đầu tiên được giải quyết):

Promise.resolve()
.then
(
  () => { throw new Error('Error occurs'); },
  err => console.log('This error is caught:', err)
);

giống hệt với:

Promise.resolve()
.catch
(
  err => console.log('This error is caught:', err)
)
.then
(
  () => { throw new Error('Error occurs'); }
)

Nhưng với lời hứa đầu tiên bị từ chối, điều này không giống nhau:

Promise.reject()
.then
(
  () => { throw new Error('Error occurs'); },
  err => console.log('This error is caught:', err)
);

Promise.reject()
.catch
(
  err => console.log('This error is caught:', err)
)
.then
(
  () => { throw new Error('Error occurs'); }
)

4
Điều này không có ý nghĩa, bạn có thể vui lòng xóa câu trả lời này không? Đó là sai lệch và mất tập trung từ câu trả lời chính xác.
Andy Ray

@AndyRay, điều này không có ý nghĩa trong ứng dụng thực tế, nhưng nó có ý nghĩa để hiểu công việc của những lời hứa.
ktretyak
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.