Vị trí đánh bắt TRƯỚC và SAU sau đó


103

Tôi khó hiểu sự khác biệt giữa đặt .catchTRƯỚC và SAU sau đó trong một lời hứa lồng nhau.

Phương án 1:

test1Async(10).then((res) => {
  return test2Async(22)
    .then((res) => {
      return test3Async(100);
    }).catch((err) => {
      throw "ERROR AFTER THEN";
    });
}).then((res) => {
  console.log(res);
}).catch((err) => {
  console.log(err);
});

Phương án 2:

test1Async(10).then((res) => {
   return test2Async(22)
     .catch((err) => {
        throw "ERROR BEFORE THEN";
      })
      .then((res) => {
        return test3Async(100);
      });
  }).then((res) => {
    console.log(res);
  }).catch((err) => {
    console.log(err);
  });

Hoạt động của mỗi chức năng như sau, test1 không thành công nếu số là <0test2 không thành công nếu số > 10và test3 không thành công nếu không phải là số 100. Trong trường hợp này, test2 chỉ thất bại.

Tôi đã cố gắng chạy và làm cho test2Async không thành công, cả TRƯỚC và SAU sau đó đều hoạt động theo cùng một cách và điều đó không thực thi test3Async. Ai đó có thể giải thích cho tôi sự khác biệt chính của việc đặt đánh bắt ở những nơi khác nhau không?

Trong mỗi chức năng, tôi console.log('Running test X')để kiểm tra xem nó có được thực thi hay không.

Câu hỏi này phát sinh do chủ đề trước tôi đã đăng Làm thế nào để biến gọi lại lồng nhau thành lời hứa? . Tôi nghĩ đó là một vấn đề khác và đáng để đăng một chủ đề khác.


cả .then và .catch đều có thể thay đổi lời hứa ... vì vậy tôi không chắc sự hiểu nhầm đến từ đâu. Nếu bạn đặt catch trước .then, nó sẽ bắt các từ chối xảy ra trước .then và .then sẽ chạy lệnh gọi lại xong / fail dựa trên những gì xảy ra trong .catch và ngược lại khi bạn hoán đổi chúng.
Kevin B

Xin lỗi nếu câu hỏi của tôi không rõ ràng. Nhưng trong trường hợp này như tôi đã nói, cả hai trường hợp đều hành xử giống nhau nên tôi không thể thấy sự khác biệt. Bạn có thể cho tôi biết khi nào chúng tôi đặt bắt TRƯỚC và khi nào chúng tôi quyết định đặt nó SAU? đặt nó sau có vẻ thực sự trực quan và phổ biến. Chỉ không chắc tại sao đôi khi chúng ta đặt nó trước sau đó
Zanko

Nếu chúng hoạt động giống nhau, đơn giản là vì những gì mỗi loại không thay đổi kết quả trong trường hợp cụ thể này. Một thay đổi nhỏ đối với một trong hai có thể thay đổi kết quả.
Kevin B

Ý bạn là gì "thay đổi kết quả". Xin lỗi tôi đang thực sự nhầm lẫn haha
Zanko

Ví dụ, nếu thay vì chỉ ra một lỗi mà bạn không làm gì cả, lời hứa sẽ chuyển từ bị từ chối sang được giải quyết. Điều đó tất nhiên sẽ thay đổi kết quả, bởi vì lời hứa bây giờ là một lời hứa đã được giải quyết chứ không phải là một lời hứa bị từ chối. (tất nhiên trừ khi nó đã được giải quyết, trong trường hợp đó thì vẫn chưa chạy được)
Kevin B

Câu trả lời:


237

Vì vậy, về cơ bản bạn đang hỏi sự khác biệt giữa hai điều này là gì (đâu plà lời hứa được tạo từ một số mã trước đó):

return p.then(...).catch(...);

return p.catch(...).then(...);

Có những khác biệt khi p giải quyết hoặc từ chối, nhưng liệu những khác biệt đó có quan trọng hay không phụ thuộc vào những gì mã bên trong .then()hoặc .catch()trình xử lý thực hiện.

Điều gì xảy ra khi pgiải quyết:

Trong lược đồ đầu tiên, khi pgiải quyết xong, .then()trình xử lý được gọi. Nếu .then()trình xử lý đó trả về một giá trị hoặc một lời hứa khác cuối cùng cũng được giải quyết, thì .catch()trình xử lý sẽ bị bỏ qua. Tuy nhiên, nếu .then()trình xử lý ném hoặc trả về một lời hứa cuối cùng bị từ chối, thì .catch()trình xử lý sẽ thực hiện cho cả một lời từ chối trong lời hứa ban đầu p, nhưng cũng có một lỗi xảy ra trong .then()trình xử lý.

Trong lược đồ thứ hai, khi pgiải quyết xong, .then()trình xử lý được gọi. Nếu .then()trình xử lý đó ném hoặc trả về một lời hứa mà cuối cùng bị từ chối, thì .catch()trình xử lý không thể nắm bắt điều đó vì nó nằm trước nó trong chuỗi.

Vì vậy, đó là sự khác biệt số 1. Nếu .catch()trình xử lý SAU, thì nó cũng có thể bắt lỗi bên trong .then()trình xử lý.

Điều gì xảy ra khi ptừ chối:

Bây giờ, trong lược đồ đầu tiên, nếu lời hứa ptừ chối, thì .then()trình xử lý bị bỏ qua và .catch()trình xử lý sẽ được gọi như bạn mong đợi. Những gì bạn làm trong .catch()trình xử lý xác định những gì được trả về là kết quả cuối cùng. Nếu bạn chỉ trả về một giá trị từ .catch()trình xử lý hoặc trả về một lời hứa cuối cùng được giải quyết, thì chuỗi hứa hẹn sẽ chuyển sang trạng thái đã giải quyết vì bạn đã "xử lý" lỗi và trở lại bình thường. Nếu bạn ném hoặc trả lại một lời hứa bị từ chối trong .catch()trình xử lý, thì lời hứa được trả lại vẫn bị từ chối.

Trong sơ đồ thứ hai, nếu lời hứa ptừ chối, thì .catch()trình xử lý được gọi. Nếu bạn trả về một giá trị bình thường hoặc một lời hứa cuối cùng được giải quyết từ .catch()trình xử lý (do đó "xử lý" lỗi), thì chuỗi hứa sẽ chuyển sang trạng thái đã giải quyết và .then()trình xử lý sau khi .catch()sẽ được gọi.

Vì vậy, đó là sự khác biệt # 2. Nếu .catch()trình xử lý là TRƯỚC, thì nó có thể xử lý lỗi và cho phép .then()trình xử lý vẫn được gọi.

Khi nào sử dụng cái nào:

Sử dụng lược đồ đầu tiên nếu bạn chỉ muốn một .catch()trình xử lý có thể bắt lỗi trong lời hứa ban đầu phoặc trong .then()trình xử lý và từ chối từ psẽ bỏ qua .then()trình xử lý.

Sử dụng lược đồ thứ hai nếu bạn muốn có thể bắt lỗi trong lời hứa ban đầu pvà có thể (tùy thuộc vào điều kiện), cho phép chuỗi hứa tiếp tục như đã được giải quyết, do đó thực thi .then()trình xử lý.

Các tùy chọn khác

Có một tùy chọn khác để sử dụng cả hai lệnh gọi lại mà bạn có thể chuyển đến .then()như trong:

 p.then(fn1, fn2)

Điều này đảm bảo rằng chỉ một trong số fn1hoặc fn2sẽ được gọi. Nếu pgiải quyết được, sau đó fn1sẽ được gọi. Nếu ptừ chối, sau đó fn2sẽ được gọi. Không có sự thay đổi kết quả nào fn1có thể làm cho fn2được gọi hoặc ngược lại. Vì vậy, nếu bạn muốn chắc chắn rằng chỉ một trong hai trình xử lý của bạn được gọi bất kể điều gì xảy ra trong chính các trình xử lý đó thì bạn có thể sử dụng p.then(fn1, fn2).


17
Câu hỏi đặc biệt là về thứ tự .then().catch()câu trả lời của bạn. Ngoài ra, bạn đưa ra một số mẹo về thời điểm sử dụng thứ tự nào, nơi tôi nghĩ phù hợp khi đề cập đến tùy chọn thứ ba, cụ thể là chuyển cả trình xử lý thành công và lỗi cho .then () . Trong trường hợp đó, nhiều nhất một trình xử lý sẽ được gọi.
ArneHugo

7
@ArneHugo - Đề xuất hay. Tôi đã thêm.
jfriend00

Vì vậy, trong quá trình Promise Chaining, chúng ta có thể viết .then .catch .catch. Sau đó là loại kịch bản nào không?
Kapil Raghuwanshi

@KapilRaghuwanshi, vâng, bạn có thể sử dụng nó để chuyển giá trị mặc định trong trường hợp không thành công. tức là Promise.reject(new Error("F")).then(x => x).catch(e => {console.log(e); return [1]}).then(console.log)Promise.resolve([2]).then(x => x).catch(e => [1]).then(console.log)
CervEd

1
@DmitryShvedov - Như tôi đoán, điều này là sai .then(this.setState({isModalOpen: false})). Bạn không chuyển một tham chiếu hàm tới .then()để mã trong parens được thực thi ngay lập tức (trước khi lời hứa được giải quyết). Nó nên được .then(() => this.setState({isModalOpen: false})).
jfriend00

31

Câu trả lời của jfriend00 là tuyệt vời, nhưng tôi nghĩ sẽ là một ý kiến ​​hay nếu thêm mã đồng bộ tương tự.

return p.then(...).catch(...);

tương tự như đồng bộ:

try {
  iMightThrow() // like `p`
  then()
} catch (err) {
  handleCatch()
}

Nếu iMightThrow()không ném, then()sẽ được gọi. Nếu nó ném (hoặc nếu then()nó ném), thì nó handleCatch()sẽ được gọi. Chú ý cách catchkhối không kiểm soát được việc có thenđược gọi hay không .

Mặt khác,

return p.catch(...).then(...);

tương tự như đồng bộ:

try {
  iMightThrow()
} catch (err) {
  handleCatch()
}

then()

Trong trường hợp này, nếu iMightThrow()không ném, thì then()sẽ thực thi. Nếu nó ném, thì tùy thuộc vào handleCatch()quyết định xem then()có được gọi hay không, vì nếu handleCatch()ném lại, thì then()sẽ không được gọi, vì ngoại lệ sẽ được ném cho người gọi ngay lập tức. Nếu handleCatch()có thể xử lý vấn đề một cách duyên dáng, thì then()sẽ được gọi.


đây là lời giải thích tốt nhưng bạn có thể quấn đứa trẻ mồ côi then()trong mộtfinally{...}
tyskr

2
@ 82Tuskers, Bạn có chắc không? Nếu tôi đưa then()vào finally{...}, nó sẽ không được gọi sai ngay cả khi handleCatch()ném? Hãy ghi nhớ rằng mục tiêu của tôi là để hiển thị mã đồng bộ tương tự, không gợi ý những cách khác nhau xử lý ngoại lệ
akivajgordon

Vì vậy, nếu chúng ta muốn xử lý tất cả các trường hợp nhưng vẫn chain .then () thì tốt nhất nên sử dụng .then (làm điều gì đó) .catch (log err và cập nhật trạng thái) .then (làm điều khác) .catch (log err) nơi chúng tôi cố gắng bắt tại mọi điểm nhưng cũng tiếp tục thực hiện các tình huống nguy hiểm?
anna
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.