10 lỗi mà các dev NodeJS thường mắc phải (phần 1)

294

Kể từ khi ra mắt thế giới lập trình, Node.js đã nhận những phản ứng trái chiều, cả khen lẫn chê. Có vẻ như tranh cãi vẫn còn tiếp diễn. Nhưng trong những cuộc tranh cãi này, chúng ta nhận ra mỗi ngôn ngữ lập trình và mỗi platform đều bị chỉ trích vì vài vấn đề nào đó xung quanh cách thức chúng ta sử dụng platform. Dù cho Node.js khiến dev khó viết code an toàn hơn hay viết concurrent code chất lượng cao, thì platform này vẫn nổi tiếng vì đã xây dựng được các dịch vụ web mạnh mẽ và hiện đại. Các dịch vụ web này scale tốt và đã chứng minh được tính ổn định, duy trì được vị trí trên Internet.

Tuy nhiên, cũng giống như bất kì platform nào khác, Node.js lại dễ “tổn thương” trước những vấn đề lập trình. Một số vấn đề làm suy giảm hiệu suất, một số khác khiến Node.js không thể sử dụng được dù bạn đã rất cố gắng. Trong bài viết này, chúng tôi sẽ điểm qua 10 lỗi mà các lập trình viên Node.js mới thường mắc phải, và cách phòng trách để trở thành 1 dev Node.js pro.

node.js developer mistakes

Lỗi #1: Ngăn event loop

JavaScript trong Node.js (tương tự như trình duyệt) cung cấp môi trường chạy single thread. Đồng nghĩa là sẽ không có 2 phần nào trong ứng dụng của bạn chạy song song, thay vào đó, bạn sẽ có cơ chế concurrency bằng cách giải quyết các I/O bound operations 1 cách riêng biệt, bất đồng bộ. Ví dụ, 1 request từ Node.js đến cơ sở dữ liệu để lấy 1 vài tài liệu nào đó sẽ cho phép Node.js tập trung vào những phần khác của ứng dụng.

node.js single threaded environment

Tuy nhiên, 1 phần code CPU-bound trong 1 instance Node.js với hàng ngàn client được kết nối sẽ ngăn event loop, sẽ để tất cả các client phải chờ. Các code CPU-bound bao gồm cách thức để sắp xếp 1 array lớn, chạy 1 loop cực dài… Ví dụ như:

Nếu bạn chạy trên array “users” nhỏ, bạn có thể gọi được hàm “sortUsersByAge” này, nhưng với 1 array lớn, nó sẽ có ảnh hưởng xấu đến hiệu năng chúng. Nếu bạn bắt buộc phải hoàn thành nó, thì bạn chắc chắn sẽ không có thứ gì khác đợi trong event loop (ví dụ: nếu đây là 1 phần của command-line tool mà bạn đang xây dựng với Node.js, và sẽ không có vấn đề gì nếu chạy riêng biệt toàn bộ), thì đây có thể không phải là vấn đề. Tuy nhiên, trong 1 server instance Node.js đang cố gắng phục vụ hàng ngàn users cùng 1 lúc thì pattern như thế có thể là hiểm họa.

Nếu array users này đang bị lấy lại từ cơ sở dữ liệu thì cách giải quyết lý tưởng sẽ là lấy array đã được phân loại sẵn trực tiếp từ cơ sở dữ liệu. Nếu event loop đang bị khóa bởi 1 loop được viết để tính toán lịch sử dữ liệu giao dịch tài chính dài thì nó có thể hoãn vài thiết lập worker/ queue bên ngoài để không cắt xén event loop.

Như bạn có thể thấy, không có giải pháp để giải quyết nhanh vấn đề này của Node.js, chỉ còn cách giải quyết từng trường hợp riêng biệt. Ý tưởng căn bản là không thực hiện các hoạt động nặng CPU trong các instances Node.js mặt trước – những instances mà các khách hàng kết nối đồng thời.

Lỗi #2: Gọi callback hơn 1 lần

JavaScript phụ thuôc vào callbacks từ rất lâu rồi. Trong các trình duyệt web, các events được giải quyết bằng cách đưa các tham chiếu đến các hàm (thường là vô danh) hoạt động như các callbacks. Trong Node.js, các callbacks từng là các yếu tố không đồng bộ duy nhất trong code có thể giao tiếp với nhau – cho đến khi Promise ra đời. Callbacks vẫn được dùng và các developers package vẫn thiết kế các APIs của mình xung quanh các callbacks. Một vấn đề Node.js thường thấy liên quan đến việc sử dụng các callbacks là gọi chúng nhiều hơn 1 lần. Thông thường, 1 hàm do 1 package cung cấp để thực hiện 1 hành động nào đó không đồng bộ được thiết kế để 1 hàm hoạt động như 1 đối số cuối cùng của nó – hàm này sẽ được gọi khi nhiệm vụ không đồng thời đã hoàn thành:

Lưu ý rằng cách thức để có 1 lệnh trả lại mỗi lần gọi “done” cho đến thời điểm cuối cùng, bởi vì gọi callback không tự động kết thúc quy trình thực hiện của hàm hiện tại. Nếu “trả lại” đầu tiên được ghi chú bên ngoài, thì khi chuyển 1 mật khẩu không dãy vẫn khiến “computeHash” bị gọi. Tùy vào cách “computeHash” giải quyết tình huống đó, mà “done” có thể bị gọi nhiều lần. Có thể bắt gặp các dev bất kì đang sử dụng hàm này từ bất kì nơi nào khi call mà dev chuyển bị gọi nhiều lần.

Chỉ cần cẩn thận, dev có thể tránh được lỗi Node.js. Một số lập trình viên Node.js theo thói quen thêm 1 từ khóa trả về mỗi lần gọi callback:

Trong các hàm không đồng bộ, giá trị trả về hầu như không có ý nghĩa gì nhiều, nên cách giải quyết này tránh được vấn đề như thế.

Lỗi #3: Nhồi nhét Callbacks

Các callbacks bị nhồi nhét sâu không được xem là vấn đề Node.js nữa mà thường được gọi là “địa ngục callback” (callback hell). Tuy nhiên, hành động này có thể khiến bạn không thể kiểm soát được dòng code:

Task càng khó khăn, thì vấn đề càng trở nên nghiêm trọng. Nhồi nhét các callbacks như thế dễ dẫn đến hậu quả là code dễ bị lỗi, khó đọc, khó duy trì. Cách giải quyết là làm rõ các tasks thành các hàm nhỏ, rồi kết nối chúng lại. Tuy nhiên, một trong những cách được cho là gọn gàng nhất là sử dụng packages Node.js tiện dụng để giải quyết các patterns Javascript không đồng bộ, như Async.js:

Tương tự như “async.waterfall”, có 1 số hàm khác do Async.js cung cấp để giải quyết các patterns không đồng bộ khác biệt. Một cách ngắn gọn, chúng tôi đã sử dụng các ví dụ đơn giản hơn ở đây, nhưng thực tế thường tồi tệ hơn.

Nguồn: toptal.com via IDE Academy lược dịch (còn tiếp)