Cho dù tất cả chúng ta có giỏi lập trình đến đâu, nhiều lúc những tập lệnh của tất cả chúng ta cũng có lỗi. Chúng hoàn toàn có thể xảy ra do lỗi của tất cả chúng ta, nguồn vào của người dùng không mong ước, phản hồi của sever bị lỗi và vì hàng ngàn nguyên do khác .
Thông thường, một đoạn script Chết (ngay lập tức dừng lại) trong trường hợp có lỗi, in nó ra console.
Bạn đang đọc: [Tự học Javascript] Xử lý lỗi, “try..catch” trong Javascript » https://final-blade.com
Nhưng có một cấu trúc cú pháp try..catch
cho phép chúng ta bắt lỗi các lỗi, vì vậy tập lệnh có thể, thay vì chết, làm điều gì đó hợp lý hơn.
Tóm Tắt
1. Cú pháp của “try…catch”
Cấu trúctry..catch
có hai khối chính: try
và sau đó catch
:
try {
// code...
} catch (err) {
// error handling
}
Nó hoạt động giải trí như thế này :
- Đầu tiên, code trong
try {...}
được thực thi. - Nếu không có lỗi, thì vào
catch(err)
, nếu khôn lỗi thì được bỏ qua: việc thực thi đến hếttry
và tiếp tục, bỏ quacatch
. - Nếu xảy ra lỗi, thì việc
try
thực thi bị dừng lại và dòng điều khiển chảy đến đầucatch(err)
. Biếnerr
(chúng ta có thể sử dụng bất kỳ tên cho nó) sẽ chứa một đối tượng lỗi với các chi tiết về những gì đã xảy ra.
Vì vậy, một lỗi bên trong khốitry {…}
không giết chết tập lệnh – chúng ta có cơ hội xử lý nó trong catch
.
Hãy xem xét một số ít ví dụ .
- Một ví dụ không có lỗi: hiển thị
alert
(1)
và(2)
:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: [email protected]
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
try {
alert('Start of try runs'); // (1) <--
// ...no errors here
alert('End of try runs'); // (2) <--
} catch(err) {
alert('Catch is ignored, because there are no errors'); // (3)
}
Một ví dụ có lỗi: hiển thị (1)
và (3)
:
try {
alert('Start of try runs'); // (1) <--
lalala; // error, variable is not defined!
alert('End of try (never reached)'); // (2)
} catch(err) {
alert(`Error has occurred!`); // (3) <--
}
try..catch
chỉ hoạt động cho các lỗi trong thời gian chạy
Để try..catch
làm việc, code phải được chạy. Nói cách khác, nó phải là JavaScript hợp lệ.
Nó sẽ không hoạt động giải trí nếu code sai về mặt cú pháp, ví dụ, nó có dấu ngoặc nhọn không khớp :
try {
{{{{{{{{{{{{
} catch(e) {
alert("The engine can't understand this code, it's invalid");
}
Công cụ JavaScript thứ nhất đọc code và sau đó chạy nó. Các lỗi xảy ra trong quy trình tiến độ đọc được gọi là lỗi thời hạn nghiên cứu và phân tích và không hề hồi sinh ( từ bên trong code đó ). Đó là chính bới động cơ không hề hiểu được code .
Vì vậy, try..catch
chỉ có thể xử lý các lỗi xảy ra trong mã hợp lệ. Các lỗi như vậy được gọi là lỗi thời gian chạy và được ..catch
bắt.
Nếu có một ngoại lệ xảy ra trong code trong quá trình chạy, như trong setTimeout
, thì try..catch
sẽ không bắt được:
try {
setTimeout(function() {
noSuchVariable; // script will die here
}, 1000);
} catch (e) {
alert( "won't work" );
}
Đó là bởi vì hàm chính được thực thi sau đó, khi động cơ đã rời khỏi cấu trúctry..catch
.
Để bắt một ngoại lệ bên trong một hàm được lên lịch, try..catch
phải nằm trong hàm đó:
setTimeout(function() {
try {
noSuchVariable; // try..catch handles the error!
} catch {
alert( "error is caught here!" );
}
}, 1000);
2. Đối tượng lỗi
Khi xảy ra lỗi, JavaScript sẽ tạo một đối tượng chứa các chi tiết về nó. Đối tượng sau đó được chuyển qua làm đối số cho catch
:
try {
// ...
} catch(err) { // <-- the "error object", could use another word instead of err
// ...
}
Đối với toàn bộ những lỗi tích hợp, đối tượng người tiêu dùng lỗi có hai thuộc tính chính :
name
Tên lỗi. Ví dụ, đối với một biến không xác định đó là "ReferenceError"
. message
Tin nhắn văn bản về cụ thể lỗi .
Có những thuộc tính không phải chuẩn khác có sẵn trong hầu hết những thiên nhiên và môi trường. Một trong những sử dụng và tương hỗ thoáng rộng nhất là :
stack
Ngăn xếp lệnh gọi hiện tại : một chuỗi với thông tin về chuỗi những cuộc gọi lồng nhau dẫn đến lỗi. Được sử dụng cho mục tiêu gỡ lỗi .
Ví dụ :
try {
lalala; // error, variable is not defined!
} catch(err) {
alert(err.name); // ReferenceError
alert(err.message); // lalala is not defined
alert(err.stack); // ReferenceError: lalala is not defined at (...call stack)
// Can also show an error as a whole
// The error is converted to string as "name: message"
alert(err); // ReferenceError: lalala is not defined
}
3. Tùy chọn “catch”
Nếu chúng ta không cần chi tiết lỗi, catch
có thể bỏ qua:
try {
// ...
} catch { // <-- without (err)
// ...
}
4. Dùng “try…catch”
Hãy khám phá một trường hợp sử dụng thực tế của try..catch
.
Như tất cả chúng ta đã biết, JavaScript tương hỗ phương pháp JSON.parse ( str ) để đọc những giá trị được code hóa JSON .
Thông thường, nó được sử dụng để giải thuật tài liệu nhận được qua mạng, từ sever hoặc nguồn khác .
Chúng tôi nhận được nó và gọi JSON.parse
như thế này:
let json = '{"name":"John", "age": 30}'; // data from the server
let user = JSON.parse(json); // convert the text representation to JS object
// now user is an object with properties from the string
alert( user.name ); // John
alert( user.age ); // 30
Bạn hoàn toàn có thể tìm thấy thông tin cụ thể hơn về JSON trong những phương pháp JSON, chương toJSON .
Nếu json
không đúng định dạng, JSON.parse
sẽ tạo ra lỗi, do đó, tập lệnh chết chết.
Chúng ta có nên hài lòng với điều đó ? Dĩ nhiên là không !
Bằng cách này, nếu có gì đó không đúng với tài liệu, khách truy vấn sẽ không khi nào biết điều đó ( trừ khi họ mở bảng điều khiển và tinh chỉnh dành cho developer ). Và mọi người thực sự không thích khi một thứ gì đó chỉ chết mà không có thông tin lỗi .
Hãy sử dụng try..catch
để xử lý lỗi:
let json = "{ bad json }";
try {
let user = JSON.parse(json); // <-- when an error occurs...
alert( user.name ); // doesn't work
} catch (e) {
// ...the execution jumps here
alert( "Our apologies, the data has errors, we'll try to request it one more time." );
alert( e.name );
alert( e.message );
}
Ở đây chúng ta chỉ sử dụng khốicatch
để hiển thị thông báo, nhưng chúng ta có thể làm nhiều hơn thế: gửi yêu cầu mạng mới, đề xuất một giải pháp thay thế cho khách truy cập, gửi thông tin về lỗi đến cơ sở ghi nhật ký, lỗi. Tất cả tốt hơn nhiều so với chỉ chết.
5. Ném lỗi của chúng ta
Điều gì xảy ra nếu json
đúng về mặt cú pháp, nhưng không có thuộc tínhname
bắt buộc ?
Như thế này :
let json = '{ "age": 30 }'; // incomplete data
try {
let user = JSON.parse(json); // <-- no errors
alert( user.name ); // no name!
} catch (e) {
alert( "doesn't execute" );
}
Ở đây JSON.parse
chạy bình thường, nhưng sự vắng mặt name
thực sự là một lỗi cho chúng ta.
Để thống nhất xử lý lỗi, chúng ta sẽ sử dụng toán tửthrow
.
6. Toán tử Throw
Các toán tửthrow
tạo ra một lỗi.
Cú pháp là :
throw
Về mặt kỹ thuật, chúng ta có thể sử dụng bất cứ thứ gì như một đối tượng lỗi. Đó có thể là ngay cả một kiểu nguyên thủy, giống như một số hoặc một chuỗi, nhưng nó tốt hơn để đối tượng sử dụng, tốt hơn với name
và thuộc tínhmessage
.
JavaScript có nhiều built-in constructors cho sai số chuẩn: Error
, SyntaxError
, ReferenceError
, TypeError
và những cái khác. Chúng ta có thể sử dụng chúng để tạo các đối tượng lỗi là tốt.
Cú pháp của họ là :
let error = new Error(message);
// or
let error = new SyntaxError(message);
let error = new ReferenceError(message);
// ...
Đối với các lỗi tích hợp (không phải cho bất kỳ đối tượng nào, chỉ cho các lỗi), thuộc tínhname
chính xác là tên của hàm tạo. Và message
được lấy từ các đối số.
Ví dụ :
let error = new Error("Things happen o_O");
alert(error.name); // Error
alert(error.message); // Things happen o_O
Hãy xem loại lỗi nào JSON.parse
tạo ra:
try {
JSON.parse("{ bad json o_O }");
} catch(e) {
alert(e.name); // SyntaxError
alert(e.message); // Unexpected token b in JSON at position 2
}
Như chúng ta có thể thấy, đó là một SyntaxError
.
Và trong trường hợp của chúng ta, sự vắng mặt name
là một lỗi, vì người dùng phải có một name
.
Vì vậy, hãy ném ra nó :
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: [email protected]
Fanpage: https://www.facebook.com/cafedevn
Group: https://www.facebook.com/groups/cafedev.vn/
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
Pinterest: https://www.pinterest.com/cafedevvn/
YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
*/
let json = '{ "age": 30 }'; // incomplete data
try {
let user = JSON.parse(json); // <-- no errors
if (!user.name) {
throw new SyntaxError("Incomplete data: no name"); // (*)
}
alert( user.name );
} catch(e) {
alert( "JSON Error: " + e.message ); // JSON Error: Incomplete data: no name
}
Trong dòng (*)
, toán tửthrow
tạo một SyntaxError
cho message
, giống như cách JavaScript sẽ tự tạo. Việc thực hiện try
ngay lập tức dừng lại và dòng điều khiển nhảy vào catch
.
Bây giờ catch
đã trở thành một nơi duy nhất cho tất cả các xử lý lỗi: cả cho JSON.parse
và các trường hợp khác.
7. Rethrowing
Trong ví dụ trên, chúng ta sử dụng try..catch
để xử lý dữ liệu không chính xác. Nhưng có thể có một lỗi không mong muốn khác xảy ra trong khối try {...}
không? Giống như một lỗi lập trình (biến không được xác định) hoặc một cái gì đó khác, không chỉ điều này dữ liệu không chính xác.
Ví dụ:
let json = '{ "age": 30 }'; // incomplete data
try {
user = JSON.parse(json); // <-- forgot to put "let" before user
// ...
} catch(err) {
alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined
// (no JSON Error actually)
}
Tất nhiên, mọi thứ đều hoàn toàn có thể ! Lập trình viên làm sai. Ngay cả trong những tiện ích nguồn mở được sử dụng bởi hàng triệu người trong nhiều thập kỷ – đùng một cái một lỗi hoàn toàn có thể được phát hiện dẫn đến những vụ hack kinh khủng .
Trong trường hợp của chúng tôi, try..catch
có nghĩa là bắt lỗi dữ liệu không chính xác về lỗi dữ liệu Nhưng theo bản chất của nó, catch
nhận được tất cả các lỗi từ try
. Ở đây, nó nhận được một lỗi không mong muốn, nhưng vẫn hiển thị cùng một thông báo"JSON Error"
. Điều đó sai và cũng làm cho code khó gỡ lỗi hơn.
May mắn thay, chúng ta có thể tìm ra lỗi nào chúng ta nhận được, ví dụ từ lỗi của nó name
:
try {
user = { /*...*/ };
} catch(e) {
alert(e.name); // "ReferenceError" for accessing an undefined variable
}
Quy tắc rất đơn thuần :
Catch chỉ nên xử lý các lỗi mà nó biết và tải lại tất cả các lỗi khác.
Kỹ thuật hoàn toàn có thể được lý giải chi tiết cụ thể hơn như :
- Bắt được tất cả các lỗi.
- Trong khối
catch(err) {...}
chúng tôi phân tích các đối tượng lỗierr
. - Nếu chúng ta không biết cách xử lý nó, chúng ta sẽ làm
throw err
.
Trong code bên dưới, chúng ta sử dụng tính năng kiểm tra lại để catch
chỉ xử lý SyntaxError
:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: [email protected]
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
let json = '{ "age": 30 }'; // incomplete data
try {
let user = JSON.parse(json);
if (!user.name) {
throw new SyntaxError("Incomplete data: no name");
}
blabla(); // unexpected error
alert( user.name );
} catch(e) {
if (e.name == "SyntaxError") {
alert( "JSON Error: " + e.message );
} else {
throw e; // rethrow (*)
}
}
Lỗi ném (*)
từ bên trong khốicatch
rơi ra khỏi try..catch
và có thể bị bắt bởi một cấu trúctry..catch
bên ngoài (nếu nó tồn tại) hoặc nó giết chết tập lệnh.
Vì vậy, khốicatch
thực sự chỉ xử lý các lỗi mà nó biết cách xử lý và bỏ qua tất cả các lỗi khác.
Ví dụ dưới đây cho thấy các lỗi như vậy có thể bị bắt bởi một cấp độ nữa try..catch
:
function readData() {
let json = '{ "age": 30 }';
try {
// ...
blabla(); // error!
} catch (e) {
// ...
if (e.name != 'SyntaxError') {
throw e; // rethrow (don't know how to deal with it)
}
}
}
try {
readData();
} catch (e) {
alert( "External catch got: " + e ); // caught it!
}
Ở đây readData
chỉ biết cách xử lý SyntaxError
, còn bên ngoài try..catch
biết cách xử lý mọi việc.
8. try…catch…finally
Đợi đã, đó không phải là toàn bộ .
Cấu rúc try..catch
có thể có thêm một mệnh đề : finally
.
Nếu nó sống sót, nó chạy trong mọi trường hợp :
- sau đó
try
, nếu không có lỗi - sau
catch
, nếu có lỗi
Cú pháp lan rộng ra trông như thế này :
try {
... try to execute the code ...
} catch(e) {
... handle errors ...
} finally {
... execute always ...
}
Hãy thử chạy code này :
try {
alert( 'try' );
if (confirm('Make an error?')) BAD_CODE();
} catch (e) {
alert( 'catch' );
} finally {
alert( 'finally' );
}
Code này có hai cách thực thi :
- Nếu bạn trả lời “Có” khi hỏi “Make an error?”, Sau đó
try -> catch -> finally
. - Nếu bạn nói là No, thì
try -> finally
.
Các điều khoảnfinally
thường được sử dụng khi chúng ta bắt đầu làm một cái gì đó và muốn hoàn thành nó trong bất kỳ trường hợp kết quả.
Chẳng hạn, chúng ta muốn đo thời gian mà hàm số Fibonacci fib(n)
. Đương nhiên, chúng ta có thể bắt đầu đo trước khi nó chạy và kết thúc sau đó. Nhưng nếu có lỗi trong khi gọi hàm thì sao? Cụ thể, việc thực hiện fib(n)
trong code dưới đây trả về lỗi cho các số âm hoặc không nguyên.
Điều khoảnfinally
này là một nơi tuyệt vời để hoàn thành các phép đo bất kể điều gì.
Ở đây finally
đảm bảo rằng thời gian sẽ được đo chính xác trong cả hai tình huống – trong trường hợp thực hiện thành công fib
và trong trường hợp có lỗi trong đó:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: [email protected]
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
let num = +prompt("Enter a positive integer number?", 35)
let diff, result;
function fib(n) {
if (n < 0 || Math.trunc(n) != n) {
throw new Error("Must not be negative, and also an integer.");
}
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}
let start = Date.now();
try {
result = fib(num);
} catch (e) {
result = 0;
} finally {
diff = Date.now() - start;
}
alert(result || "error occurred");
alert( `execution took ${diff}ms` );
Bạn có thể kiểm tra bằng cách chạy code bằng cách nhập 35
vào prompt
– nó sẽ thực thi bình thường, try
sau đófinally
. Và sau đó nhập -1
– sẽ có một lỗi ngay lập tức và việc thực thi sẽ diễn ra 0ms
. Cả hai phép đo đều được thực hiện chính xác.
Nói cách khác, hàm có thể kết thúc bằng return
hoặc throw
, điều đó không quan trọng. Điều khoản thực thifinally
trong cả hai trường hợp.
Các biến là cục bộ bên trong try..catch..finally
Xin lưu ý rằng result
và diff
các biến trong code ở trên được khai báo trước try..catch
.
Mặt khác, nếu chúng ta khai báo let
trong khốitry
, nó sẽ chỉ hiển thị bên trong nó.finally
và return
finally
làm việc cho bất kỳ lối ra từ try..catch
. Điều đó bao gồm một rõ ràng một return
.
Trong ví dụ dưới đây, có một return
trong try
. Trong trường hợp này, finally
được thực thi ngay trước khi điều khiển trở về code bên ngoài.
function func() {
try {
return 1;
} catch (e) {
/* ... */
} finally {
alert( 'finally' );
}
}
alert( func() ); // first works alert from finally, and then this one
try..finally
Cấu trúctry..finally
, không có mệnh đềcatch
, cũng hữu ích. Chúng ta áp dụng nó khi chúng ta không muốn xử lý lỗi ở đây (để chúng xảy ra), nhưng muốn chắc chắn rằng các quy trình mà chúng ta đã bắt đầu được hoàn tất.
function func() {
// start doing something that needs completion (like measurements)
try {
// ...
} finally {
// complete that thing even if all dies
}
}
Trong đoạn code trên, một lỗi bên trong try
luôn rơi ra, bởi vì không có catch
. Nhưng finally
hoạt động trước khi dòng thực thi rời khỏi hàm.
9. Catch toàn cầu
Đặc thù môi trường
tin tức từ phần này không phải là một phần của JavaScript cốt lõi .
Hãy tưởng tượng chúng ta đã có một lỗi nghiêm trọng bên ngoài try..catch
và kịch bản đã chết. Giống như một lỗi lập trình hoặc một số điều khủng khiếp khác.
Có cách nào để phản ứng về những sự cố như vậy không ? Chúng ta hoàn toàn có thể muốn ghi lại lỗi, hiển thị một cái gì đó cho người dùng ( thường thì họ không thấy thông tin lỗi ), v.v.
Không có gì trong đặc tả, nhưng môi trường thường cung cấp nó, bởi vì nó thực sự hữu ích. Chẳng hạn, Node.js process.on("uncaughtException")
dành cho điều đó. Và trong trình duyệt, chúng ta có thể gán một hàm cho thuộc tính window.onerror đặc biệt, nó sẽ chạy trong trường hợp có lỗi.
Cú pháp :
window.onerror = function(message, url, line, col, error) {
// ...
};
message
Thông báo lỗi.
url
URL của tập lệnh nơi xảy ra lỗi.
line
, col
Số dòng và cột nơi xảy ra lỗi.
error
Đối tượng lỗi.
Ví dụ :
Vai trò của trình xử lý toàn cầu window.onerror
thường không phải là phục hồi thực thi tập lệnh – Nó không thể làm gì với code trong trường hợp có lỗi lập trình, nhưng gửi thông báo lỗi cho developer.
Ngoài ra còn có những dịch vụ web cung ứng ghi nhật ký lỗi cho những trường hợp như vậy, như https://errorception.com or http://www.muscula.com .
Họ thao tác như thế này :
- Chúng ta đăng ký tại dịch vụ và nhận một đoạn code (hoặc URL tập lệnh) từ chúng để chèn vào các trang.
- Tập lệnh JS đó thiết lập một
window.onerror
chức năng tùy chỉnh . - Khi xảy ra lỗi, nó sẽ gửi một yêu cầu mạng về nó tới dịch vụ.
- Chúng ta có thể đăng nhập vào giao diện web dịch vụ và thấy lỗi.
10. Tóm lược
Cấu trúctry..catch
cho phép xử lý lỗi thời gian chạy. Theo nghĩa đen, nó cho phép dùng thử các ứng dụng và chạy các lỗi có thể xảy ra trong đó.
Cú pháp là :
try {
// run this code
} catch(err) {
// if an error happened, then jump here
// err is the error object
} finally {
// do in any case after try/catch
}
Có thể không có phầncatch
hoặc không finally
, vì vậy các cấu trúc ngắn hơn try..catch
và try..finally
cũng hợp lệ.
Các đối tượng người tiêu dùng lỗi có những thuộc tính sau :
message
– thông báo lỗi có thể đọc được của con người.name
– chuỗi có tên lỗi (tên hàm xây dựng lỗi).stack
(không chuẩn, nhưng được hỗ trợ tốt) – ngăn xếp tại thời điểm tạo lỗi.
Nếu không cần một đối tượng lỗi, chúng ta có thể bỏ qua nó bằng cách sử dụng catch {
thay vì catch(err) {
.
Chúng ta cũng có thể tạo ra lỗi của chúng ta bằng cách sử dụng toán tử throw
. Về mặt kỹ thuật, đối số throw
có thể là bất cứ điều gì, nhưng thông thường, đó là một đối tượng lỗi kế thừa từ lớpError
tích hợp. Xem Thêm về mở rộng lỗi trong chương tiếp theo.
Rethrowing là một mô hình xử lý lỗi rất quan trọng: một khối catch
thường mong đợi và biết cách xử lý loại lỗi cụ thể, vì vậy nó nên điều chỉnh lại các lỗi mà nó không biết.
Ngay cả khi chúng ta không có try..catch
, hầu hết các môi trường đều cho phép chúng ta thiết lập trình xử lý lỗi toàn cầu để bắt lỗi khi mà rơi ra. Trong trình duyệt, đó là window.onerror
.
Full series tự học Javascript từ cơ bản tới nâng cao tại đây nha .
Nếu bạn thấy hay và có ích, bạn hoàn toàn có thể tham gia những kênh sau của cafedev để nhận được nhiều hơn nữa :
Chào thân ái và quyết thắng!
Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!
Source: https://final-blade.com
Category: Kiến thức Internet