thử bắt trong javascript không phải là một thực hành tốt?


69

Có một điều khoản cho khối thử bắt trong javascript . Mặc dù trong java hoặc bất kỳ ngôn ngữ nào khác, bắt buộc phải xử lý lỗi, tôi không thấy ai sử dụng chúng trong javascript ở mức độ lớn hơn. Đó không phải là một thực tiễn tốt hay chỉ là chúng ta không cần chúng trong javascript?


2
While in java or *any other language* it is mandatory to have error handling...- Không hẳn. Java, vâng, nhưng có rất nhiều ngôn ngữ không khăng khăng bắt thử (như C #).
Jim G.

Đó là bởi vì bạn không thể sử dụng chúng trong môi trường không đồng bộ. Tôi sử dụng chúng thường xuyên bằng mã đồng bộ hóa ở mức độ trừu tượng thấp hơn, ví dụ bằng cách chuyển đổi một cái gì đó thành một cái gì đó, v.v ...
inf3rno

Tôi cá là bạn sẽ thấy nhiều lần thử trong mã phía máy chủ hơn mã phía máy khách. Hầu hết các mã phía bạn bè mà bạn không biết là không quan trọng. Tôi muốn giảm thiểu KB xuống đường ống quan trọng hơn việc khôi phục từ mọi lỗi - trong hầu hết các ứng dụng và hoàn cảnh phía trình duyệt.
Svidgen

Có một cách khác để tránh thử bắt bằng cách sử dụng có thể và Hoặc (monads) chúng tôi đã có một trình quét web được viết bằng nút. Chúng tôi đã có thể loại bỏ tất cả các thử bắt bằng cách sử dụng nó.
dùng93

Câu trả lời:


66

Người ta phải tránh các throwlỗi như là cách để vượt qua các điều kiện lỗi xung quanh trong các ứng dụng.

Các throwtuyên bố chỉ nên được sử dụng "Đối với điều này không bao giờ xảy ra, tai nạn và cháy. Đừng hồi phục thanh lịch trong bất kỳ cách nào"

try catch tuy nhiên được sử dụng trong trường hợp các đối tượng máy chủ hoặc ECMAScript có thể gây ra lỗi.

Thí dụ:

var json
try {
    json = JSON.parse(input)
} catch (e) {
    // invalid json input, set to null
    json = null
}

Các khuyến nghị trong cộng đồng node.js là bạn chuyển các lỗi xung quanh trong các cuộc gọi lại (Vì các lỗi chỉ xảy ra đối với các hoạt động không đồng bộ) làm đối số đầu tiên

fs.readFile(uri, function (err, fileData) {
    if (err) {
        // handle
        // A. give the error to someone else
        return callback(err)
        // B. recover logic
        return recoverElegantly(err)
        // C. Crash and burn
        throw err
    }
    // success case, handle nicely
})

Ngoài ra còn có các vấn đề khác như thử / bắt thực sự tốn kém và nó xấu và đơn giản là nó không hoạt động với các hoạt động không đồng bộ.

Vì vậy, do các hoạt động đồng bộ không nên gây ra lỗi và nó không hoạt động với các hoạt động không đồng bộ, nên không ai sử dụng thử bắt ngoại trừ các lỗi được ném bởi các đối tượng máy chủ hoặc ECMAScript


2
Tôi sẽ không đi xa để nói rằng không ai sử dụng thử bắt, đó chỉ là công cụ sai cho công việc trong hầu hết các trường hợp. Khi có những trường hợp thực sự đặc biệt, có thể đáng để ném Error, nhưng chúng rất ít và xa.
zzzzBov

7
@zzzzBov Không có gì sai khi ném lỗi cho trường hợp C, sự cố và ghi. Tôi chỉ không nghĩ rằng bạn nên bắt lỗi và phục hồi. Ví dụ: document.getElementByIdkhông ném khi phần tử không tồn tại, nó chỉ trả về null. Điều tương tự có thể được thực hiện cho hầu hết các trường hợp
Raynos

4
@Raynos, ném từ một chức năng đồng bộ là hoàn toàn chấp nhận được và có ý nghĩa trong trường hợp sử dụng đó. Trả lại lỗi cho cuộc gọi lại là không đồng bộ vì lỗi ném là đồng bộ hóa.
sbartell

@Raynos có lời khuyên nào tốt về việc tránh sử dụng Lỗi / Ngoại lệ để kiểm soát luồng trên phía máy khách (môi trường không phải là Nút)? Tôi đã tìm thấy bài đăng này trên StackOverflow , nhưng nó chủ yếu hướng đến Java
blong

@ b.long đơn giản. đừng ném lỗi bao giờ. Vấn đề được giải quyết.
Raynos

32

Thử / bắt trong Javascript không phải là chống đạn như trong các ngôn ngữ khác, do tính chất không đồng bộ của Javascript. Hãy xem xét đoạn trích này:

try {
    setTimeout(function() {
        do_something_that_throws();
    }, 1000);
}
catch (e) {
    alert("You won't see this!");
}

Vấn đề là luồng điều khiển rời khỏi trykhối trước khi do_something_that_throws()được thực thi, do đó lỗi được ném bên trong hàm gọi lại không bao giờ bị bắt.

Vì vậy, thử / bắt về cơ bản là không phù hợp trong nhiều trường hợp và không phải lúc nào cũng rõ ràng liệu có thứ gì đó thực thi mã không đồng bộ hay không. May mắn thay, javascript với thành ngữ gọi lại đơn luồng không đồng bộ đơn đặc biệt và hỗ trợ của nó cho các bao đóng thực tế cung cấp một giải pháp thay thế thanh lịch: xử lý lỗi kiểu tiếp tục truyền. Chỉ cần chuyển đáp ứng thích hợp cho bất kỳ lỗi nào dưới dạng hàm, ví dụ:

setTimeout(function () {
    do_something_that_calls_err(function(err) {
        alert("Something went wrong, namely this: " + err);
    }),
    1000);

5
nó cũng không phải là bằng chứng trong các ngôn ngữ khác. Chuyển mã đó sang bất kỳ ngôn ngữ nào hỗ trợ các cuộc gọi lại không đồng bộ và nó cũng sẽ thất bại.
Raynos

2
@Raynos: Bạn nói đúng; tuy nhiên, các ngôn ngữ khác (hay đúng hơn là các nền văn hóa hỗ trợ của chúng) không mua mạnh vào thành ngữ gọi lại không đồng bộ như Javascript, và do tính chất đa luồng của hầu hết các môi trường khác, các thiếu sót của thử / bắt là rõ ràng hơn và được bao quanh bởi rất nhiều cảnh báo khác, vì vậy mọi người tự nhiên bước đi cẩn thận trong các tình huống gọi lại đa luồng / không đồng bộ, và nhận thức rõ hơn về những cạm bẫy tiềm ẩn.
tdammers

Đây có thực sự là do "bản chất không đồng bộ" của JavaScript không? Theo như tôi có thể thấy, về mặt lý thuyết, Try / Catch có thể được tạo ra để hoạt động theo phương diện từ vựng giống như cách các định danh được đóng từ vựng; nó chỉ không xảy ra để làm việc theo cách đó trong JavaScript.
Brian Gordon

2
Trong node.js> = 0.8 đáng chú ý là có thể thử / bắt không đồng bộ, vui lòng xem stackoverflow.com/questions/14301839/
Benjamin Gruenbaum

Là lựa chọn duy nhất để bắt thử đồng bộ là kiểu truyền tiếp tục không đồng bộ?
CMCDragonkai

23

Rất nhiều câu trả lời này hơi cũ và không tính đến các tính năng ES7 mới asyncawait.

Sử dụng async/ awaitbây giờ bạn có thể nhận được luồng điều khiển không đồng bộ như bạn muốn:

async function email(address) {
  try {
    // Do something asynchronous that may throw...
    await sendEmail({ to: address, from: 'noreply@domain.com`, subject: 'Hello' })
  } catch(err) {
    if (err instanceof SomeCustomError) {
      elegantlyHandleError(err)
    } else {
      throw err
    } 
  }
})

Tìm hiểu thêm về async/ awaittại đây . Bạn có thể sử dụng async/ awaitbây giờ sử dụng babel .


Nhưng nó tương tự như một mã đồng bộ
Atul Agrawal

@AtulAgrawal vâng, đó là điểm. Async / await cho phép bạn viết mã async theo kiểu đồng bộ để bạn có thể tránh "địa ngục gọi lại" và kết nối nhiều lời hứa với nhau. Theo ý kiến ​​của tôi, đó là một cách rất thú vị để viết mã async
Dana Woodman

Vì vậy, lợi ích của việc sử dụng javascript là gì nếu chúng ta tạo mã async thành đồng bộ. bởi vì hầu hết mọi người sử dụng javascript do bản chất không đồng bộ của nó
Atul Agrawal

2
@AtulAgrawal bạn có được cả hai thế giới tốt nhất - bạn thích bản chất không đồng bộ của ngôn ngữ (vì đoạn mã trên vẫn sẽ được thực thi không đồng bộ - giải phóng chuỗi và tất cả) và tách các lợi ích của kiểu mã hóa thân thiện với lập trình viên hơn. Không phải là không có vấn đề, mặc dù ...
ZenMaster

15

try-Catch trong javascript cũng hợp lệ và hữu ích như trong bất kỳ ngôn ngữ nào khác thực hiện chúng. Có một lý do chính khiến nó không được sử dụng nhiều trong javascript như trong các ngôn ngữ khác. Lý do tương tự của nó javascript được xem là một ngôn ngữ kịch bản xấu xí, lý do tương tự như lý do tại sao mọi người nghĩ rằng các lập trình viên javascript không phải là lập trình viên thực sự:

  • Javascript là một ngôn ngữ cực kỳ dễ tiếp cận và phổ biến

Thực tế là rất nhiều người tiếp xúc với javascript (nhờ ngôn ngữ được hỗ trợ duy nhất bởi các trình duyệt) có nghĩa là bạn có rất nhiều mã không chuyên nghiệp ngoài kia. Tất nhiên cũng có nhiều lý do nhỏ:

  • một số thứ trong javascript không đồng bộ và do đó không catchthể (nội dung không đồng bộ)
  • đã có nhiều cuộc thảo luận quá mức về việc làm thế nào để thử bắt có hiệu suất rất lớn. Nó có một chút thành công về hiệu năng , nhưng đối với hầu hết các mã, nó rất đáng giá.
  • javascript đã (không may) được triển khai theo cách thường âm thầm bỏ qua các lỗi (ví dụ tự động thay đổi chuỗi thành số)

Bất kể, nên sử dụng thử bắt , nhưng tất nhiên bạn nên học cách sử dụng chúng đúng cách - giống như mọi thứ khác trong lập trình.


3
Và tại sao bạn đã downvote xuống, oh downvoter im lặng?
dùng250878

Đã đồng ý. Tôi nghĩ rằng câu trả lời được chấp nhận nói chung là đúng, nhưng có những lý do chính đáng để sử dụng thử bắt, và thậm chí ném, trừ khi làm việc với các đối tượng bản địa. Khi một lỗi được đưa ra thay vì chuyển đến một cuộc gọi lại, nó sẽ dừng thực thi thêm và nhảy lên ngăn xếp đến khối đầu tiên hơn là có thể xử lý nó. Đây là một lợi thế lớn và có ý nghĩa hoàn hảo cho các hoạt động đồng bộ, đặc biệt nếu chúng liên quan đến việc làm tổ sâu. Có vẻ điên rồ khi đề xuất các ngoại lệ là "xấu" (tốt, ngoài những lý do rõ ràng) - chúng thực sự là một công cụ rất hữu ích với một "sức mạnh" độc đáo.
Dấu chấm phẩy

2
Bạn đã bao giờ dành thời gian để đọc mã nguồn của jQuery, chưa? Hầu như không có bất kỳ thử bắt nào trong đó ngoại trừ chính xác các kịch bản được đề cập trong câu trả lời được chấp nhận: để phát hiện tính năng và sử dụng các đối tượng gốc / máy chủ có lỗi. Để gọi mã không sử dụng try-Catch nhiều (như jQuery) 'không chuyên nghiệp' có vẻ ngớ ngẩn. Thành thật mà nói, tôi nghĩ rằng các lập trình viên Javascript đặc biệt mới đến từ Java có xu hướng sử dụng quá mức các tính năng ngôn ngữ như thử bắt.
Stijn de Witt

6

Tôi tin rằng phần lớn lý do try..catchhiếm gặp trong JavaScript là do ngôn ngữ có khả năng chịu lỗi khá cao. Phần lớn các tình huống có thể được xử lý bằng cách sử dụng kiểm tra mã, mặc định tốt và các sự kiện không đồng bộ. Trong một số trường hợp, chỉ cần sử dụng một mẫu sẽ ngăn chặn các vấn đề:

function Foo() {
    //this may or may not be called as a constructor!!
    //could accidentally overwrite properties on window
}

function Bar() {
    if (!(this instanceof Bar)) {
        return new Bar();
    }
    //this will only work on Bar objects, and wont impact window
}

Một số vấn đề chính trong các ngôn ngữ khác khiến ngoại lệ xảy ra chỉ đơn giản là không tồn tại trong JS. Kiểu đúc không cần thiết trong phần lớn thời gian. Thay vào đó, phương thức ưa thích thường là kiểm tra tính năng (thực thi một giao diện cụ thể):

function doFoo(arg) {
    if (arg.foo) {
        arg.foo();
    } else {
        Bar.prototype.foo.call(arg);
    }
}

Với việc thêm async/ awaitvào ngôn ngữ, try..catchngày càng trở nên phổ biến. Hứa hẹn là hình thức không đồng bộ của try..catch, nó có ý nghĩa mà người ta nên mong đợi:

doSomething().then(
  doSomethingWithResult,
  doSomethingWithError
)

thay vào đó được viết là:

try {
  const result = await doSomething()
  doSomethingWithResult(result)
} catch (e) {
  doSomethingWithError(e)
}

3

Có thể một lý do khác khiến try / Catch không được sử dụng nhiều trong Javascript là cấu trúc không có sẵn trong các phiên bản Javascript đầu tiên ... nó đã được thêm vào sau.

Do đó, một số trình duyệt cũ không hỗ trợ nó. (Trên thực tế, nó có thể gây ra lỗi trình phân tích cú pháp / cú pháp trong một số trình duyệt cũ hơn, một điều khó khăn hơn trong việc "lập trình phòng thủ" chống lại hầu hết các loại lỗi khác.)

Quan trọng hơn, vì ban đầu nó không có sẵn, các hàm dựng sẵn Javascript được phát hành ban đầu (cái mà người ta sẽ gọi là các hàm "thư viện" trong nhiều ngôn ngữ) không sử dụng nó. (Nó không hoạt động tốt khi "bắt" một lỗi từ someobject.somefunction () nếu nó không "ném" mà thay vào đó chỉ trả về "null" khi gặp sự cố.)


Tuy nhiên, một lý do có thể khác là cơ chế thử / bắt ban đầu dường như không cần thiết (và dường như vẫn chưa hữu ích lắm). Nó chỉ thực sự cần thiết khi các cuộc gọi được lồng thường xuyên ở nhiều cấp độ sâu; chỉ cần trả lại một số loại ERRNO sẽ hoạt động tốt cho các cuộc gọi trực tiếp (mặc dù để làm cho nó thực sự hữu ích bất cứ khi nào có sẵn, thực tiễn tốt nhất trong hầu hết các ngôn ngữ là sử dụng nó ở mọi nơi thay vì chỉ thực hiện các cuộc gọi lồng nhau). Vì logic Javascript ban đầu được dự kiến ​​là nhỏ và đơn giản (xét cho cùng, nó chỉ là một công cụ bổ trợ cho trang web :-), các lệnh gọi hàm không được dự kiến ​​sẽ được lồng sâu và do đó, cơ chế thử / bắt không cần thiết.


3
Không không không, hoàn toàn không phải vậy. Động cơ cũ không còn quan trọng nữa trong một thời gian dài và cộng đồng javascript nói chung nhanh chóng bỏ đi những thứ cũ khi được chứng minh. Tôi thậm chí sẽ đặt cược rằng phần lớn các nhà phát triển javascript hiện nay là hậu IE6.
Camilo Martin

0

Tôi tin rằng chúng không được sử dụng nhiều vì việc ném ngoại lệ vào mã phía máy khách Javascript khiến việc gỡ lỗi trang trở nên khó khăn hơn.

Thay vì ném ngoại lệ, tôi thường thích hiển thị hộp cảnh báo al, (nghĩa là alert("Error, invalid...");)

Nghe có vẻ lạ, nhưng lỗi Javascript xảy ra ở phía khách hàng, nếu khách hàng đang sử dụng trang bạn đã tạo và trang đó ném ngoại lệ, trừ khi khách hàng là một người hiểu biết về công nghệ, không có cách nào anh ta có thể nói bạn có vấn đề gì

Anh ta sẽ chỉ gọi cho bạn khi nói: "Này, trang X không hoạt động!", Và sau đó, tất cả sẽ tùy thuộc vào bạn để tìm ra những gì nó đã sai và ở đâu trong mã.

Bằng cách sử dụng hộp cảnh báo thay vì nó có thể xảy ra nhiều hơn mà ông gọi và nói điều gì đó như: "Này, khi tôi bấm vào nút A của trang X nó cho thấy một hộp thoại mà nói ..." , hãy tin tôi, nó sẽ được dễ dàng hơn để tìm thấy con bọ.

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.