2013-08-23 28 views
5

Tôi đang gặp khó khăn khi hiểu cách xử lý nội dung nào đó có vẻ như là một khía cạnh khá cơ bản về diễn đạt. Nếu tôi có một số mã ném một ngoại lệ trong một cuộc gọi lại không đồng bộ, không có cách nào tôi có thể bắt ngoại lệ đó bởi vì khối try/catch không còn trong phạm vi thời gian gọi lại đang chạy. Trong những tình huống này, trình duyệt sẽ treo cho đến khi cuối cùng bỏ cuộc nói rằng máy chủ không hồi đáp. Đây là trải nghiệm người dùng rất xấu. Tôi sẽ có thể trả lại ngay một lỗi 500 cho khách hàng. Trình xử lý lỗi nhanh mặc định dường như không xử lý tình huống này. Dưới đây là một số mã mẫu:Xử lý ngoại lệ theo cách rõ ràng

var express = require("express"); 

var app = express(); 
app.use(app.router); 
//express error handler (never called) 
app.use(function(err, req, res, next) { 
    console.log(err); 
    res.send(500); 
}); 

app.get("/test", function(req, res, next) { 
    require("fs").readFile("/some/file", function(err, data) { 
     a.b(); //blow up 
    }); 
}); 

app.listen(8888); 

Trong đoạn mã trên, dòng a.b() ném một ngoại lệ "Tham chiếuError: a chưa được xác định". Trình xử lý lỗi được định nghĩa không bao giờ được gọi. Lưu ý rằng đối tượng err được trả về bởi fs.readFile() là null trong trường hợp này vì tệp đã được đọc chính xác. Lỗi là mã bên trong trình xử lý async.

Tôi đã đọc this post về việc sử dụng uncaughtExpception của nút ngay cả, nhưng the documentation nói không sử dụng phương pháp đó. Ngay cả khi tôi đã sử dụng nó, làm thế nào sau đó tôi sẽ gửi trả lời 500 cho người dùng? Các đối tượng phản ứng nhanh không còn xung quanh để tôi sử dụng.

Vậy bạn xử lý tình huống này như thế nào?

+0

Tại sao lỗi được đưa ra trong cuộc gọi lại? Nhiều khả năng, MongooseObject sẽ ném một lỗi nội bộ, nơi bạn sẽ bắt nó, và sau đó 'callback (err)'. Bây giờ gọi lại được hiển thị ở trên có một đối tượng lỗi giả và bạn có thể gửi trả lời 500. – Plato

+0

Nếu Mongoose được vào tình huống mà nó KHÔNG gọi lại với một lỗi ... hmm Tôi dunno có lẽ bạn có thể làm cho một wrapper setTimeout. nhưng tôi hy vọng tất cả các lỗi sẽ được chuyển ngay đến hàm gọi lại – Plato

+0

Nó không phải là Mongoose Tôi lo lắng, tôi chỉ sử dụng nó như một cách để gọi một hàm async. Tôi lo lắng nhiều hơn về mã của riêng tôi đang làm một cái gì đó câm và gây ra một ngoại lệ để được ném. Tôi sẽ cập nhật mã mẫu để làm rõ hơn. – d512

Trả lời

4

OK, tôi sẽ đăng một câu trả lời hoàn toàn khác vì câu trả lời đầu tiên của tôi có một số thông tin có giá trị nhưng lại không có chủ đề.

Câu trả lời ngắn: điều chính xác cần làm là những gì đã xảy ra: chương trình của bạn nên in theo dõi ngăn xếp và thoát với lỗi.

Những suy nghĩ tiềm ẩn:

Vì vậy, tôi nghĩ rằng bạn cần phải suy nghĩ về lỗi trong các chuyên mục khác nhau. Câu trả lời đầu tiên của tôi đề cập đến các lỗi liên quan đến dữ liệu mà một chương trình được viết tốt có thể và nên xử lý một cách rõ ràng. Những gì bạn mô tả là một CRASH. Nếu bạn đọc tài liệu về node.js mà bạn đã liên kết, nó là chính xác. Điều hữu ích duy nhất mà chương trình của bạn có thể làm tại thời điểm này là thoát với một dấu vết ngăn xếp và cho phép một người giám sát quá trình khởi động lại nó và đạt được một trạng thái đã hiểu. Một khi chương trình của bạn đã bị hỏng, về cơ bản là không thể phục hồi do phạm vi lỗi rất rộng có thể là nguyên nhân gốc rễ của một ngoại lệ nhận được lên trên cùng của ngăn xếp. Trong ví dụ cụ thể của bạn, lỗi này sẽ tiếp tục diễn ra mỗi lần cho đến khi lỗi mã nguồn được sửa và ứng dụng được triển khai lại. Nếu bạn lo lắng rằng mã chưa được kiểm tra và lỗi sẽ đi vào ứng dụng của bạn, việc thêm mã xử lý lỗi chưa được kiểm tra và lỗi không thực sự giải quyết được vấn đề đúng. Tuy nhiên, trong ngắn hạn, không có cách nào để tham chiếu đến đối tượng yêu cầu HTTP gây ra ngoại lệ này, vì vậy AFAIK bạn không thể thay đổi cách thức người dùng cuối nhận ra trong trình duyệt, ngoài việc xử lý nó tại một lớp proxy ngược trung gian nơi bạn có thể định cấu hình thời gian chờ thô và gửi một trang lỗi thân thiện hơn (tất nhiên sẽ vô dụng đối với bất kỳ yêu cầu nào không phải là tài liệu HTML đầy đủ).

Kinh Thánh của Xử lý lỗi trong Node

Error Handling in Node.js bởi Dave Pacheco là công việc dứt khoát về vấn đề này theo ý kiến ​​của tôi. Nó là toàn diện, rộng lớn và toàn diện. Tôi khuyên bạn nên đọc và đọc lại định kỳ.


Để giải quyết nhận xét của @ asparagino, nếu một ngoại lệ không thể xử lý được dễ dàng lặp lại hoặc xảy ra với tần suất cao, không phải là trường hợp ngoại lệ, đó là lỗi. Điều đúng đắn cần làm là cải thiện mã của bạn để không tạo ra các ngoại lệ không bị bắt gặp khi đối mặt với tình huống này. Trên thực tế xử lý các điều kiện, do đó chuyển đổi một lỗi lập trình thành một lỗi hoạt động, và chương trình của bạn có thể tiếp tục mà không cần khởi động lại và không có một ngoại lệ uncaught.

+0

Cảm ơn câu trả lời Peter. Đây là những gì tôi nghi ngờ từ những gì tôi đã đọc. Tôi không chắc tôi đồng ý 100% với ý tưởng rằng điều hữu ích duy nhất mà một chương trình có thể làm khi đối mặt với một ngoại lệ bất ngờ là khởi động lại. Rõ ràng là từ góc độ người dùng cuối, đó không phải là trải nghiệm tốt. Thêm vào đó, nó sẽ ảnh hưởng đến tất cả các yêu cầu khác đang diễn ra tại thời điểm đó. Tôi đã viết rất nhiều ứng dụng web sử dụng trình xử lý ngoại lệ cấp cao nhất để trả lại 500, ghi nhật ký lỗi nhưng vẫn duy trì và chạy dịch vụ. Dù sao, nó hoạt động theo cách nó hoạt động, tôi sẽ chỉ phải đối phó với nó. Cảm ơn một lần nữa. – d512

+0

Vâng tôi nghĩ rằng đây là một hậu quả không thể tránh khỏi của sự kiện dựa trên IO. Bản chất uber-động của JavaScript cũng làm cho các lỗi dễ dàng xâm nhập vào sản xuất hơn. Có lẽ các câu trả lời khác sẽ xuất hiện với một số gợi ý tốt. –

+3

@PeterLyons - Quy trình nút thường sẽ xử lý hàng chục hoặc thậm chí hàng nghìn yêu cầu đồng thời. Mỗi trong số này có thể chứa dữ liệu có giá trị và sẽ có một chi phí nếu nó không hoàn thành. Bạn đang xây dựng một kẻ tung hứng. Nó có nên thả tất cả các quả bóng ngay khi nó rơi xuống không? Lỗi và ngoại lệ phải được ghi lại. Nhật ký cần được xem xét và các điều kiện lỗi và cạnh được theo dõi và sửa lỗi. Quá trình này không nên bỏ thuốc lá. – asparagino

4

Bạn nên sử dụng phần mềm trung gian xử lý lỗi của express thông qua app.use(error, req, res, next). Express duy trì một ngăn xếp phần mềm trung gian riêng biệt mà nó sử dụng khi ngăn xếp trung gian bình thường ném một ngoại lệ không bị bắt. (Lưu ý bên rằng chỉ nhìn vào trạng thái của hàm gọi lại (số đối số dự kiến) để phân loại nó như là phần mềm trung gian thông thường hoặc lỗi xử lý trung gian, một chút huyền diệu, vì vậy hãy nhớ rằng bạn phải khai báo các đối số như trên để hiểu rõ đây là lỗi xử lý phần mềm trung gian). Và dựa trên câu hỏi và nhận xét của bạn, chỉ cần hiểu rằng ngoại lệ không phải tất cả đều hữu ích trong node.js bởi vì mọi cuộc gọi async đều nhận được chồng mới, đó là lý do tại sao cuộc gọi lại được sử dụng ở mọi nơi và đối số thứ nhất là lỗi phổ biến . Khối try/catch của bạn trong ví dụ này sẽ chỉ bắt một ngoại lệ được ném trực tiếp bởi findById (như nếu id không được xác định, vì nó nằm trong đoạn mã của bạn), nhưng sau khi thực hiện cuộc gọi đến cơ sở dữ liệu, hơn và không có ngoại lệ hơn nữa có thể xảy ra cho đến khi một ngăn xếp cuộc gọi hoàn toàn khác nhau bắt đầu khi nút gọi ra gọi lại không đồng bộ IO.

Thanks for the answer, but this only works if I put the try/catch inside the async callback and have the catch do next(exp). I'd like to avoid having separate try/catch blocks in every single async callback.

Không, điều đó không đúng. Bạn không phải gọi thủ công next(exp). Express sẽ bắt lỗi và kích hoạt phần mềm trung gian xử lý lỗi cho bạn (đó là các trang báo cáo ngoại lệ thân thiện với nhà phát triển trong chế độ dev). Và các thư viện không đồng bộ không ném ngoại lệ ngay cả trong điều kiện lỗi "bình thường". Họ vượt qua một lỗi để gọi lại, do đó, nói chung bạn không phải bận tâm với try/catch mà nhiều trong nút. Chỉ cần không bao giờ bỏ qua một tham số lỗi được chuyển đến một hàm gọi lại, và bạn vẫn ổn.

Bạn không thấy soạn sẵn này trong nút:

someDb.query(someCriteria, function (error, result) { 
    try { 
    //some code to deal with result 
    } catch (exception) { 
    callback(exception); 
    } 
}); 

Bạn thấy điều này mặc dù:

someDb.query(someCriteria, function (error, result) { 
    if (error) { 
    callback(error); 
    return; 
    } 
    //some code to deal with result 
}); 

Node xử lý IO khác nhau, có nghĩa là các cuộc gọi stack hoạt động khác nhau, có nghĩa là trường hợp ngoại lệ hoạt động khác nhau, có nghĩa là xử lý lỗi hoạt động khác nhau. Bạn có thể viết một nút ổn định/ứng dụng thể hiện xử lý lỗi mà không bị lỗi mà không cần viết một lần thử/nắm bắt đơn lẻ. (Express có một trong đó xử lý các lỗi vô hình mà bong bóng tất cả các cách để đầu trang).Nó không phải là một hạn chế chức năng, nó chỉ là một sự bảo trợ của IO không đồng bộ có nghĩa là bạn phải viết mã của bạn một cách khác nhau và xử lý lỗi với callbacks thay vì ngoại lệ. Nghĩ về nó như là một "giới hạn" trái ngược với "cách nó là" đang đặt một ý nghĩa tiêu cực về cái gì đó thực sự chỉ là một thực tế kỹ thuật. Có các mẫu sạch và mạnh mẽ để xử lý ngoại lệ trong cả hai mô hình đồng bộ và không đồng bộ.

+0

Cảm ơn câu trả lời, nhưng điều này chỉ hoạt động nếu tôi đặt try/catch bên trong callback async và có catch làm tiếp theo (exp). Tôi muốn tránh có các khối try/catch riêng biệt trong mọi cuộc gọi lại không đồng bộ. – d512

+0

Vâng, tôi nghĩ rằng tôi hiểu vấn đề, tôi chỉ không biết phải làm gì về nó. Tại một số điểm, mã sẽ ném một ngoại lệ và tôi không muốn quá trình nút bị treo hoặc treo. Đây có phải chỉ là một giới hạn vốn có của nút không? – d512

+0

Peter, cảm ơn đã dành thời gian để đăng câu trả lời kỹ lưỡng. Hoặc là tôi không giải thích được vấn đề của tôi khá đúng hay tôi đang làm điều gì sai trong mã xử lý lỗi của tôi. Bạn nói "Express sẽ bắt lỗi và kích hoạt lỗi xử lý phần mềm trung gian cho bạn" nhưng tôi dường như không thể thực hiện công việc này. Tôi đã sửa lại câu hỏi của mình để chứa một ví dụ đơn giản nhưng hoàn chỉnh thể hiện những gì tôi thấy. – d512