Xử lý ngoại lệ với Promise

Xử lý ngoại lệ với Promise

Bài viết này sẽ mô tả một số cách xử lý ngoại lệ với Promise. Cùng tìm hiểu nhé!

Xử lý ngoại lệ với Promise

Xử lý ngoại lệ với Promise

Promise chain rất tốt trong việc xử lý lỗi. Khi một Promises bị từ chối, chương trình sẽ chuyển đến trình xử lý lỗi gần nhất. Điều đó rất thuận tiện trong thực tế.

Ví dụ: trong mã bên dưới URL để fetch dữ liệu bị sai (không có trang web nào như vậy) và .catch xử lý lỗi:

fetch('https://no-such-server.blabla') // rejects .then(response => response.json()) .catch(err => alert(err)) // TypeError: failed to fetch (the text may vary)
Code language: JavaScript (javascript)

Như bạn có thể thấy, .catch không nhất thiết phải có ngay lập tức. Nó có thể xuất hiện sau một hoặc có thể nhiều .then

Hoặc, có thể, mọi thứ đều ổn với trang web, nhưng response không phải là JSON hợp lệ. Cách dễ nhất để bắt tất cả các lỗi là thêm .catch vào cuối chuỗi:

fetch('/article/promise-chaining/user.json') .then(response => response.json()) .then(user => fetch(`https://api.github.com/users/${user.name}`)) .then(response => response.json()) .then(githubUser => new Promise((resolve, reject) => { let img = document.createElement('img'); img.src = githubUser.avatar_url; img.className = "promise-avatar-example"; document.body.append(img); setTimeout(() => { img.remove(); resolve(githubUser); }, 3000); })) .catch(error => alert(error.message));
Code language: JavaScript (javascript)

Thông thường, .catch như vậy hoàn toàn không kích hoạt. Nhưng nếu bất kỳ Promise nào ở trên bị reject (sự cố mạng hoặc json không hợp lệ hoặc bất cứ điều gì), thì nó sẽ bắt được nó.

Xử lý ngoại lệ với Promise

Try…catch ẩn

new Promise((resolve, reject) => { throw new Error("Whoops!"); }).catch(alert); // Error: Whoops!
Code language: JavaScript (javascript)

hoặc

new Promise((resolve, reject) => { reject(new Error("Whoops!")); }).catch(alert); // Error: Whoops!
Code language: JavaScript (javascript)

sẽ tự động bắt lỗi và biến nó thành Promise bị reject. Điều này xảy ra không chỉ trong hàm thực thi mà còn trong các trình xử lý của nó. Nếu chúng ta ném vào bên trong một trình xử lý .then, điều đó có nghĩa là một Promise bị từ chối, do đó, điều khiển sẽ chuyển đến trình xử lý lỗi gần nhất.

Đây là một ví dụ:

new Promise((resolve, reject) => { resolve("ok"); }).then((result) => { throw new Error("Whoops!"); // rejects the promise }).catch(alert); // Error: Whoops!
Code language: JavaScript (javascript)

Điều này xảy ra đối với tất cả các lỗi, không chỉ những lỗi do câu lệnh throw gây ra. Ví dụ, một lỗi lập trình:

new Promise((resolve, reject) => { resolve("ok"); }).then((result) => { blabla(); // no such function }).catch(alert); // ReferenceError: blabla is not defined
Code language: JavaScript (javascript)

.catch cuối cùng không chỉ bắt được các reject mà còn cả các lỗi ngẫu nhiên trong các trình xử lý ở trên.

Xử lý ngoại lệ với Promise

Rethrowing

Như chúng ta đã nhận thấy, .catch ở cuối chuỗi tương tự như try..catch. Chúng ta có thể có bao nhiêu trình xử lý .then tùy thích và sau đó sử dụng một .catch duy nhất ở cuối để xử lý lỗi trong tất cả chúng.

Chúng ta có thể phân tích lỗi và khắc phục lỗi nếu không thể xử lý được. Điều tương tự cũng có thể xảy ra đối với những Promise.

Nếu chúng ta throw vào bên trong .catch, thì điều khiển sẽ chuyển đến trình xử lý lỗi gần nhất tiếp theo. Và nếu chúng ta xử lý lỗi và kết thúc bình thường, thì nó sẽ tiếp tục đến trình xử lý .then thành công gần nhất tiếp theo.

Trong ví dụ bên dưới, .catch đã xử lý lỗi thành công:

// the execution: catch -> then new Promise((resolve, reject) => { throw new Error("Whoops!"); }).catch(function(error) { alert("The error is handled, continue normally"); }).then(() => alert("Next successful handler runs"));
Code language: JavaScript (javascript)

Tại đây, khối .catch kết thúc bình thường. Vì vậy, trình xử lý .then thành công tiếp theo được gọi.

Trong ví dụ dưới đây, chúng ta thấy tình huống khác với .catch. Trình xử lý (*) bắt lỗi và không thể xử lý nó (ví dụ: nó chỉ biết cách xử lý URIError), vì vậy nó lại ném nó:

// the execution: catch -> catch new Promise((resolve, reject) => { throw new Error("Whoops!"); }).catch(function(error) { // (*) if (error instanceof URIError) { // handle it } else { alert("Can't handle such error"); throw error; // throwing this or another error jumps to the next catch } }).then(function() { /* doesn't run here */ }).catch(error => { // (**) alert(`The unknown error has occurred: ${error}`); // don't return anything => execution goes the normal way });
Code language: JavaScript (javascript)

Việc thực thi sẽ nhảy từ .catch (*) đầu tiên sang .catch tiếp theo (**) trong chuỗi.

Lỗi không được xử lý

Điều gì xảy ra khi lỗi không được xử lý? Ví dụ: chúng ta đã quên thêm .catch vào cuối chuỗi, như sau:

new Promise(function() { noSuchFunction(); // Error here (no such function) }) .then(() => { // successful promise handlers, one or more }); // without .catch at the end!
Code language: JavaScript (javascript)

Trong trường hợp có lỗi, Promise sẽ bị từ chối và việc thực thi sẽ chuyển đến trình xử lý từ chối gần nhất. Nhưng không có. Vì vậy, lỗi bị ‘kẹt’. Không có mã nào để xử lý nó.

Trong thực tế, giống như với các lỗi thông thường trong mã, điều đó có nghĩa là có gì đó đã xảy ra sai lầm nghiêm trọng.

Điều gì sẽ xảy ra khi một lỗi thông thường xảy ra và không được bắt bằng try..catch? Tập lệnh chết với một thông báo trong bảng điều khiển. Điều tương tự cũng xảy ra với những Promise không có kết quả.

JavaScript Engine theo dõi những reject như vậy và tạo ra lỗi chung trong trường hợp đó. Bạn có thể thấy nó trong Console nếu bạn chạy ví dụ trên.

Trong trình duyệt, chúng ta có thể gặp các lỗi như vậy bằng cách sử dụng unhandledrejection:

window.addEventListener('unhandledrejection', function(event) { // the event object has two special properties: alert(event.promise); // [object Promise] - the promise that generated the error alert(event.reason); // Error: Whoops! - the unhandled error object }); new Promise(function() { throw new Error("Whoops!"); });
Code language: JavaScript (javascript)

Sự kiện là một phần của HTML tiêu chuẩn.

Nếu lỗi xảy ra và không có .catch, unhandledrejection sẽ kích hoạt và nhận đối tượng sự kiện với thông tin về lỗi, vì vậy chúng tôi có thể làm điều gì đó.

Thông thường các lỗi như vậy là không thể khôi phục được, vì vậy cách tốt nhất của chúng tôi là thông báo cho người dùng về sự cố và có thể báo cáo sự cố cho máy chủ.

Trong các môi trường không phải trình duyệt như Node.js, có nhiều cách khác để theo dõi các lỗi chưa được khắc phục.

Cảm ơn bạn đã theo dõi bài viết!

Các bạn có thể tham khảo các bài viết hay về JavaScript tại đây.


Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.

TỔNG HỢP TÀI LIỆU HỌC LẬP TRÌNH CƠ BẢN CHO NGƯỜI MỚI BẮT ĐẦU

KHOÁ HỌC BOOTCAMP JAVA/JAVASCRIPT/PHP TRỞ THÀNH LẬP TRÌNH VIÊN TRONG 5-6 THÁNG

Bình luận