Lỗi lặp lại trong bắt lời hứa


88

Tôi đã tìm thấy mã sau trong một hướng dẫn:

promise.then(function(result){
    //some code
}).catch(function(error) {
    throw(error);
});

Tôi hơi bối rối: cuộc gọi bắt có thực hiện được gì không? Đối với tôi, dường như nó không có bất kỳ tác dụng nào, vì nó chỉ đơn giản là ném ra cùng một lỗi đã bị bắt. Tôi căn cứ vào cách thức hoạt động của một lần thử / bắt thông thường.


Bạn có thể cung cấp một liên kết đến hướng dẫn? Có thể có ngữ cảnh bổ sung sẽ hữu ích ...
Igor

@ Tôi không thể, đó là trên Pluralsight. Đây có thể chỉ là một trình giữ chỗ cho một số logic xử lý lỗi?
Tyler Durden

Đó là những gì tôi đoán vì nó không làm gì hơn sau đó chuyển lỗi cho người gọi, điều này cũng có thể được thực hiện bằng cách không bắt đầu.
Igor

1
@TylerDurden Tôi nghi ngờ rằng bạn nói đúng về việc nó là một trình giữ chỗ.
Jared Smith

@TylerDurden, tôi cũng đoán rằng đó là một trình giữ chỗ. Có thể đang cố gắng trình bày cách định dạng / chuẩn hóa lỗi. Về cơ bản, tương đương với lời hứa try { ... }catch(error){ throw new Error("something went wrong") }. Hoặc để cho thấy Lời hứa và Lỗi tương thích với nhau (ít nhất là theo cách đó) . Nhưng trong cách triển khai hiện tại, nó thật ngu ngốc. Bạn nói đúng, nó không làm gì cả và nó thậm chí không giống như một hook bạn thêm vào OOP để cho phép ghi đè nó trong một lớp kế thừa. Tôi sẽ thêm khối bắt ngay khi nó thực hiện điều gì đó, nhưng không phải như vậy, không chỉ như một trình giữ chỗ.
Thomas

Câu trả lời:


124

Không có ích gì để bắt và ném như bạn thể hiện. Nó không làm bất cứ điều gì hữu ích ngoại trừ thêm mã và thực thi chậm. Vì vậy, nếu bạn sắp sửa .catch()và đánh lại, bạn nên có điều gì đó mà bạn muốn làm trong đó .catch(), nếu không, bạn chỉ nên xóa .catch()hoàn toàn.

Điểm thông thường cho cấu trúc chung đó là khi bạn muốn thực thi một cái gì đó trong .catch()ví dụ như ghi lại lỗi hoặc dọn dẹp một số trạng thái (như đóng tệp), nhưng bạn muốn chuỗi hứa tiếp tục khi bị từ chối.

promise.then(function(result){
    //some code
}).catch(function(error) {
    // log and rethrow 
    console.log(error);
    throw error;
});

Trong một hướng dẫn, nó có thể chỉ để cho mọi người thấy họ có thể bắt lỗi ở đâu hoặc dạy khái niệm xử lý lỗi, sau đó sửa lại lỗi đó.


Một số lý do hữu ích cho việc bắt và trồng lại như sau:

  1. Bạn muốn ghi lại lỗi , nhưng vẫn giữ chuỗi lời hứa bị từ chối.
  2. Bạn muốn biến lỗi thành một số lỗi khác (thường để xử lý lỗi dễ dàng hơn ở cuối chuỗi). Trong trường hợp này, bạn sẽ tạo lại một lỗi khác.
  3. Bạn muốn thực hiện một loạt xử lý trước khi chuỗi hứa tiếp tục (chẳng hạn như tài nguyên đóng / miễn phí) nhưng bạn muốn chuỗi hứa tiếp tục bị từ chối.
  4. Bạn muốn có một vị trí để đặt điểm ngắt cho trình gỡ lỗi tại thời điểm này trong chuỗi hứa hẹn nếu có lỗi.

Tuy nhiên, việc bắt và lặp lại cùng một lỗi mà không có mã nào khác trong trình xử lý bắt không có ích gì cho việc chạy bình thường của mã.


Theo quan điểm của tôi, đó là một ví dụ điển hình. Với cách tiếp cận như vậy, bạn dễ dàng nhận được nhiều bản ghi cho 1 lỗi. Trong java, bạn có thể chỉ throw new Exception(periousException);tôi không biết liệu javascript có hỗ trợ lỗi lồng nhau hay không, nhưng dù sao thì "đăng nhập và ném" là một cách thực hành không tốt.
Cherry

26
@Cherry - Bạn không thể nói rằng đây là một thực hành xấu. Đôi khi một mô-đun muốn ghi lại các lỗi của chính nó theo cách riêng của nó và đây là một cách để làm điều đó. Bên cạnh đó, tôi không khuyến nghị điều này, tôi chỉ giải thích rằng không có lý do gì để có .catch()và ném cùng một lỗi bên trong phần bắt trừ khi bạn thực hiện SOMETHING khác trong .catch(). Đó là điểm của câu trả lời này.
jfriend00 11/09/17

Nói chung các ngoại lệ phải phù hợp với mức độ trừu tượng. Hoàn toàn OK khi bắt một ngoại lệ liên quan đến db, và ném một cái gì đó như một ngoại lệ "dịch vụ" sẽ được xử lý bởi người gọi. Điều này đặc biệt hữu ích khi bạn không để lộ chi tiết về trường hợp ngoại lệ ở mức độ thấp
maxTrialfire

3
Một lý do tốt khác để bắt và (đôi khi) ném là xử lý một lỗi cụ thể, nhưng ném lại mọi thứ khác.
Jasper

2
@SimonZyx - Có, .finally()có thể rất hữu ích cho việc đó, nhưng đôi khi tài nguyên đã được xử lý trong đường dẫn không lỗi nên .catch()vẫn là nơi để đóng chúng. Nó thực sự phụ thuộc vào tình hình.
jfriend00

15

Cả hai phương thức .then().catch()phương thức đều trả về Promises, và nếu bạn ném một Ngoại lệ vào một trong hai trình xử lý, thì lời hứa trả về sẽ bị từ chối và Ngoại lệ sẽ bị bắt trong trình xử lý từ chối tiếp theo.

Trong đoạn mã sau, chúng tôi đưa ra một ngoại lệ trong đoạn mã đầu tiên .catch(), ngoại lệ bị bắt trong đoạn mã thứ hai .catch():

new Promise((resolve, reject) => {
    console.log('Initial');

    resolve();
})
.then(() => {
    throw new Error('Something failed');
        
    console.log('Do this'); // Never reached
})
.catch(() => {
    console.log('Something failed');
    throw new Error('Something failed again');
})
.catch((error) => {
    console.log('Final error : ', error.message);
});

Câu thứ hai .catch()trả về một Promised đã được hoàn thành, .then()trình xử lý có thể được gọi là:

new Promise((resolve, reject) => {
    console.log('Initial');

    resolve();
})
.then(() => {
    throw new Error('Something failed');
        
    console.log('Do this'); // Never reached
})
.catch(() => {
    console.log('Something failed');
    throw new Error('Something failed again');
})
.catch((error) => {
    console.log('Final error : ', error.message);
})
.then(() => {
    console.log('Show this message whatever happened before');
});

Tham khảo hữu ích: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#Chaining_ after_a_catch

Hi vọng điêu nay co ich!


4

Không có sự khác biệt quan trọng nếu bạn loại bỏ catchhoàn toàn cuộc gọi phương thức.

Điều duy nhất nó thêm vào là một microtask bổ sung, trong thực tế có nghĩa là bạn sẽ nhận thấy việc từ chối lời hứa muộn hơn so với trường hợp một lời hứa thất bại mà không có catchđiều khoản.

Đoạn mã tiếp theo thể hiện điều này:

var p;
// Case 1: with catch
p = Promise.reject('my error 1')
       .catch(function(error) {
          throw(error);
       });

p.catch( error => console.log(error) );
// Case 2: without catch
p = Promise.reject('my error 2');

p.catch( error => console.log(error) );

Lưu ý cách từ chối thứ hai được báo cáo trước lần đầu tiên. Đó là về sự khác biệt duy nhất.


3

Vì vậy, có vẻ như câu hỏi của bạn là, "Trong chuỗi lời hứa, .catch()phương pháp làm gì?"

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw

Câu lệnh ném "sẽ dừng (các câu lệnh sau khi ném sẽ không được thực thi) và quyền điều khiển sẽ được chuyển đến khối bắt đầu tiên trong ngăn xếp cuộc gọi. Nếu không có khối lệnh bắt nào tồn tại giữa các hàm người gọi, chương trình sẽ kết thúc."

Trong chuỗi hứa hẹn, .then()phương thức sẽ trả về một số loại đoạn dữ liệu. Sự trở lại này của chunk sẽ hoàn thành lời hứa. Việc trả về dữ liệu thành công hoàn thành lời hứa. Bạn có thể nghĩ về .catch()phương pháp theo cách tương tự. .catch()tuy nhiên sẽ xử lý những lần truy xuất dữ liệu không thành công. Câu lệnh ném hoàn thành lời hứa. Đôi khi, bạn sẽ thấy các nhà phát triển sử dụng .catch((err) => {console.log(err))} nó cũng sẽ hoàn thành chuỗi hứa hẹn.


0

Bạn thực sự không cần phải ném lại nó, chỉ cần để trống Promise.catch nếu không nó sẽ coi như bỏ xử lý từ chối và sau đó bọc mã trong một lần bắt thử và nó sẽ tự động bắt lỗi được truyền xuống.

try{
  promise.then(function(result){
    //some code
  }).catch(function(error) {
    //no need for re throwing or any coding. but leave this as this otherwise it will consider as un handled
  });
}catch(e){
  console.log(e);
  //error can handle in here
}

0

Trong chuỗi hứa hẹn, tốt hơn là sử dụng .catch

ex trong hàm f2: .then (...). catch (e => remove (e));

  • test1 - với thử bắt
  • test2 - không cần thử hoặc .catch
  • test3 - với .catch

function f1() {
    return new Promise((resolve, reject) => {
        throw new Error('test');
    });
}

function f2() {
    return new Promise((resolve, reject) => {
        f1().then(value => {
            console.log('f1 ok ???');
        }).catch(e => reject(e));
    });
}

function test1() {
    console.log('test1 - with try catch - look in F12');
    try {
      f2().then(() => { // Uncaught (in promise) Error: test
        console.log('???'); });
    } catch (e) {
      console.log('this error dont catched');
    }
}

function test2() {
    console.log('test2 - without try or .catch - look in F12');
    f2(); // Uncaught (in promise) Error: test
}

function test3() {
  console.log('test3 - with .catch');
  f2().then(value => {
    console.log('??');
  }).catch(e => {
    console.log(' now its ok, error ', e);
  })
}

setTimeout(() => { test1(); 
  setTimeout(() => { test2(); 
    setTimeout(() => { test3(); 
    }, 100);
  }, 100);
}, 100);

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.