Một số phương pháp xử lý lỗi (error handling) trong Node.js

Một số phương pháp xử lý lỗi (error handling) trong Node.js

Tin ngắn hàng ngày dành cho bạn
  • Manus đã chính thức mở cửa cho tất cả người dùng rồi đấy mọi người. Cho những ai chưa biết thì đây là một công cụ viết báo cáo (làm mưa làm gió) giống như Deep Research của OpenAI á. Mỗi ngày được miễn phí 300 Credits để nghiên cứu. Mỗi lượt nghiên cứu tiêu tốn tuỳ thuộc vào độ phức tạp của yêu cầu. À với cả họ đang có chương trình tặng miễn phí Credits hay sao á. Như mình thì vào thấy được hẳn 2000.

    Mình dùng thử, so sánh với cùng một lệnh giống như đợt trước dùng bên Deep Research thì nội dung khác biệt nhau hoàn toàn. Manus báo cáo như kiểu viết văn hơn so với OpenAI là các gạch đầu dòng và bảng biểu.

    À lúc đăng ký xong có bắt nhập số điện thoại để xác minh, nếu lỗi thì các bạn đợi qua ngày thử lại xem có được không nhé.

    » Xem thêm
  • Mọi người chắc nghe nhiều về xu hướng tìm kiếm thông tin bằng AI chứ không cần công cụ tìm kiếm như Google nữa rồi đúng không? Không đâu xa ánh xạ vào bản thân thì thấy đúng thật, thi thoảng mới tìm kiếm thôi chứ còn đâu toàn hỏi tụi AI.

    Ngay từ đầu viết blog, thứ mà mình hướng đến là chia sẻ kinh nghiệm chứ không phải là những bài mang nặng tính kỹ thuật, máy móc, hướng dẫn từ đầu... Vì thời điểm đó đã có quá nhiều người làm nội dung này rồi và họ làm rất tốt, tại sao mình phải cố phát minh lại bánh xe? Một điều nữa là tin tưởng độc giả của mình có khả năng tìm hiểu vấn đề. Nếu bạn đọc đủ nhiều các bài viết trên blog thì thấy mình luôn cố gắng chèn thêm các liên kết tham khảo ngoài bài viết, nêu ra vấn đề mở và rất ít khi kết luận chắc chắn một điều gì đó.

    Mình đã cố gắng rèn luyện kỹ năng viết, kỹ năng trình bày và cả cách tương tác với độc giả để mang lại giá trị cho họ. Nhiều lúc ngồi lật lại các con số thống kê thấy lượng đọc bài viết tăng lên lại cảm thấy vui. Nhưng khi nguồn truy cập đến từ Google thì lại thấy buồn, vì điều đó chứng tỏ họ biết đến mình chỉ khi đang cố đi tìm giải pháp, có thể họ chỉ đọc chớp nhoáng, may ra tìm được cách giải quyết và thế là đóng cửa sổ trình duyệt rồi đi như một cơn gió.

    Chừng vài tháng đổ lại đây, một điều khiến mình rất vui đó là lượng người truy cập thẳng vào trang chủ mà không thông qua công cụ tìm kiếm đang tăng dần lên, có nhiều hôm lượng truy cập tự nhiên còn cao hơn cả đến từ Google. Điều đó chứng tỏ độc giả đã có thói quen quay lại trang của mình nhiều hơn và họ tìm thấy được giá trị từ blog mang lại. Vui mừng khôn xiết 🤩

    Bên cạnh đó thì lượng truy cập vào chuyên mục Threads - tức là mục mình đang viết bài này đang cao hơn bao giờ hết. Điều đó chứng tỏ xu hướng đi theo tin nhanh là đúng đắn. Mình có thể ngồi cả ngày để viết tin ngắn cho bạn đọc vì nó rất nhanh mà tiện, không tốn công đi tìm tài liệu để viết, không tốn cả thời gian viết nữa, còn mình thì có rất nhiều thứ để chia sẻ 😅. Nhưng không vì thế mà bỏ bê các bài viết dài, vì dài thì có nhiều thông tin để chia sẻ hơn.

    Vài lời tâm sự thế thôi chứ hơn một tháng nay mình chưa viết bài viết mới nào vì công việc bận quá. Xong lâu dần cứ trì hoãn lại thành lười. À với cả tháng 5 này rất thích hợp để đọc các cuốn sách về cách mạng á. Có hôm đọc đến 2 giờ sáng mới đi ngủ 🥱

    » Xem thêm
  • Mình mới nhìn thấy một trang web khá thú vị nói về các cột mốc đáng nhớ trong lịch sử phát triển Internet toàn cầu: Internet Artifacts

    Chỉ từ 1977 - khi Internet còn nằm trong hộp thí nghiệm thì nhìn xem - giờ đây Internet đã khiến mọi thứ phát triển đến mức nào 🫣

    » Xem thêm

Vấn đề

Lỗi là một vấn đề tồn tại song song với việc phát triển ứng dụng, tôi hay nói vui là chừng nào còn code thì còn "bug". Lỗi cũng có lỗi "this" lỗi "that", có lỗi chúng ta hoàn toàn nhận biết được chúng sẽ xảy ra trong tương lai, bên cạnh đó là những lỗi trời ơi đất hỡi mà chúng ta hoàn toàn không biết khả năng xuất hiện của chúng.

Lỗi gây ra những phiền toái, đôi khi gây ra những hậu quả nghiêm trọng. Chính vì thế xử lý lỗi luôn là vấn đề quan trọng trong lập trình. Bài viết hôm nay tôi xin trình bày một số phương pháp xử lý lỗi một cách "duyên dáng" trong môi trường Node.js.

Các phương pháp

Sử dụng async/await hoặc promise để xử lý lỗi ở các hàm không đồng bộ

Tránh sử dụng callback để rơi vào callback hell khiến mã của bạn trở nên lồng nhau và gây rối mắt, khó khăn cho bảo trì.

Ví dụ một trường hợp sử dụng callback để xử lý lỗi:

getData(someParameter, function(err_a, a) {
    if(err_a!== null) {
        getMoreDataA(a, function(err_b, b) {
            if(err_b !== null) {
                getMoreDataB(b, function(err_c, c) {
                    getMoreDataC(c, function(err_d, d) {
                        if(err_d !== null ) {
                            // do something
                        }
                    })
                });
            }
        });
    }
});

Thay vào đó hãy sử dụng promise để xử lý lỗi một cách "duyên dáng" hơn:

return getData(someParameter)
    .then(getMoreDataA)
    .then(getMoreDataB)
    ...
    .catch(err => handle(err));

Tuy nhiên promise có gây khó khăn trong khi debug, hãy sử dụng cú pháp "thanh lịch" của async/await kết hợp với try/catch:

try {
    const a = await getData(someParameter);
    const b = await getData(someParameter);
} catch(err) {
    handle(err);
}

Trả ra một đối tượng Error thay vì bất kì đối tượng có kiểu nào khác khi muốn báo lỗi.

Vì tính dễ dãi của JS chúng ta có thể "throw" ra tuỳ ý một đối tượng nào để báo lỗi, nó có thể là một số, một chuỗi hay một object nhưng đừng nên làm như thế. Hãy throw ra một Error để đảm bảo tính đồng nhất mã của bạn và với các thư viện, hơn nữa Error còn lưu giữ thông tin quan trọng như StackTrace là vị trí gây ra lỗi.

/ / đừng làm thế này
if (!condition) {
    throw ('điều kiện không hợp lệ');
}

// mà hãy làm thế này
if (!condition) {
    throw new Error('điều kiện không hợp lệ');
}

// hay thậm chí bạn có thể làm tốt hơn bằng cách tạo ra một đối tượng lỗi
// mang thêm nhiều thông tin hữu ích khác
function MyError(name, description, ...args) {
    Error.call(this);
    Error.captureStackTrace(this);
    this.name = name;
    this.description = description;
    ...
};

MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;

if (!condition) {
    throw new MyError('CONDITION_NOT_VALID', 'điều kiện không hợp lệ');
}

Xử lý lỗi tập trung

Tạo ra một hoặc nhiều hàm sẵn sàng nhận vào một đối tượng lỗi sau đó thì phân tích hay phân phát lỗi đến các nơi khác, tránh xử lý riêng lẻ khiến cho mã của bạn trở nên khó kiểm soát.

Tưởng tượng bạn sẽ làm gì khi có lỗi xảy ra? Log ra console, gửi chúng đến các dịch vụ theo dõi (tracking) hay logging, ghi ra file, kiểm tra điều kiện và phân loại lỗi... rất nhiều thứ cần làm với một lỗi được bắn ra vì thế hãy tập hợp chúng lại trong một hoặc nhiều hàm chuyên để xử lý lỗi.

Thận trọng với lỗi unhandledRejection

Đây là một lỗi rất khó chịu trong Node.js, nó có thể khiến cho ứng dụng của bạn bị treo và không thể tiếp tục xử lý bất kì yêu cầu nào được nữa. Lỗi này xảy ra khi bạn xử lý Promise không đúng cách, cụ thể là không có hàm xử lý lỗi của reject trong Promise.

UserModel.findByPk(1).then((user) => {
  if(!user)
      throw new Error('user not found');
});

Để bắt được lỗi này, chúng ta cần sử dụng process.on.

process.on('unhandledRejection', (reason, p) => {
  throw reason;
});

process.on('uncaughtException', (error) => {
  errorManagement.handler.handleError(error);
  if (!errorManagement.handler.isTrustedError(error))
    process.exit(1);
});

process.on thường được tạo ngay index.js nơi mà ứng dụng của bạn khởi động đầu tiên để nó có thể "lắng nghe" bất kì tín hiệu nào từ việc xử lý Promise không đúng cách. Để từ đó có phương án xử lý thích hợp tránh máy chủ bị treo.

Đừng bao giờ tin dữ liệu đầu vào, luôn xác thực chúng.

Chúng ta cung cấp những endpoint POST, PUT... để cho phép client truyền dữ liệu lên, có một điều là dữ liệu không thể đảm bảo luôn luôn đúng. Vì thế cách an toàn nhất là luôn validate dữ liệu nhận được, ngăn chặn hành vi vô tình hay cố ý gửi sai dữ liệu mong muốn có thể gây ra lỗi hay cố tình phá hoại hệ thống.

Có nhiều thư viện rất tốt giúp chúng ta làm điều này như joi hay là ajv.

const schema = Joi.object({
  username: Joi.string()
    .alphanum()
    .min(3)
    .max(30)
    .required(),

  password: Joi.string()
    .pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')),
});

let body = { username: '2coffee', password: '2coffee' };
schema.validate(body); // -> { value: { username: '2coffee', password: '2coffee' } }

let body = {};
schema.validate(body); // -> { value: {}, error: '"username" is required' }

Sử dụng trình ghi log chuyên nghiệp

Ghi log là một cách có thể theo dõi và giám sát những lỗi đã xảy ra trong quá khứ để có thể tra cứu lại bất kì lúc nào. Tôi có một bài viết riêng nói về việc ghi log trong ứng dụng Node.js, bạn đọc xem thêm tại Logging ứng dụng viết bằng node.js qua 3 cấp độ.

Hãy viết Unit test

Unit test là một trong những phương pháp giúp bạn phát hiện ra lỗi sớm nhất trong quá trình phát triển. Tuy mất nhiều thời gian để viết Unit test nhưng nó rất đáng giá để đầu tư thời gian của bạn. Một khi Unit test hoạt động tốt nó sẽ tiết kiệm cho bạn rất nhiều thời gian và chi phí sau này đấy.

"Thoát" ứng dụng khi có thể

Trong trường hợp đối diện với những lỗi đặc thù trước mắt chưa có cách nào chữa trị hoặc bắt được lỗi nhưng khó có thể khôi phục trạng thái ứng dụng thì cách xử lý tạm thời là hãy khiến cho ứng dụng bị crash và nhờ các công cụ DevOps khởi động lại. Đây có thể không phải là cách hay nhưng việc khởi động lại ứng dụng của bạn sẽ khôi phục lại được trạng thái trước khi xảy ra lỗi.

Sử dụng AMP

Application Performance Management (APM) được dùng để giám sát và quản lý hiệu suất cũng như theo dõi tính khả dụng của ứng dụng. APM cố gắng phát hiện và chuẩn đoán các vấn đề về hiệu suất ứng dụng và kịp thời thông báo cho bạn biết khi có vấn đề gì xảy ra.

Hiện tại có khá nhiều sản phẩm, dịch vụ APM trên thị trường từ Open Source, miễn phí đến trả phí. Tính năng cũng rất phong phú từ đơn giản đến phức tạp, theo dõi bằng việc "ping" hoặc tích hợp sâu vào theo dõi hệ thống phức tạp.

Hãy bắt đầu với một dịch vụ APM đơn giản như uptimerobot.com. Nó theo dõi thời gian uptime ứng dụng bằng cách gửi một yêu cầu sau mỗi 5 phút và chờ phản hồi thành công hay thất bại.

uptimerobot.com

Newrelic là một công cụ theo dõi toàn diện hơn vì nó theo dõi được nhiều thứ hơn như ngăn xếp lỗi chi tiết, thời gian phản hồi, nút thắt cổ chai và thống kê chi tiết...

newrelic

Tổng kết

Trên đây là một số phương pháp xử lý lỗi đối với ứng dụng viết bằng Node.js mà tôi đề xuất. Có thể ngoài những cách trên còn có nhiều phương pháp khác mà tôi chưa biết, nếu bạn phát hiện ra còn thiếu xin hãy để lại dưới phần bình luận.

Cao cấp
Hello

5 bài học sâu sắc

Mỗi sản phẩm đi kèm với những câu chuyện. Thành công của người khác là nguồn cảm hứng cho nhiều người theo sau. 5 bài học rút ra được đã thay đổi con người tôi mãi mãi. Còn bạn? Hãy bấm vào ngay!

Mỗi sản phẩm đi kèm với những câu chuyện. Thành công của người khác là nguồn cảm hứng cho nhiều người theo sau. 5 bài học rút ra được đã thay đổi con người tôi mãi mãi. Còn bạn? Hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.

Bình luận (1)

Nội dung bình luận...
Avatar
Jess Vanes2 năm trước
Hướng dẫn dùng mấy tool apm đi ạ
Trả lời
Avatar
Xuân Hoài Tống2 năm trước
@gif [ISOckXUybVfQ4] Chắc là sẽ hơi lâu ạ