JavaScript là khái niệm của các exceptions. Sự giống nhau về syntax của hầu hết tất cả các ngôn ngữ truyền thống với hỗ trợ xử lý exceptions, như Java và C++, JavaScript có thể “ném” (throws) và bắt (catch) các exceptions trong các khối try-catch:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function slugifyUsername(username) {
if(typeof username === ‘string’) {
throw new TypeError(‘expected a string username, got '+(typeof username))
}
// ...
}
try {
var usernameSlug = slugifyUsername(username)
} catch(e) {
console.log(‘Oh no!’)
}
|
Tuy nhiên, try-catch sẽ không hoạt động như bạn muốn trong các tình huống không đồng bộ. Ví dụ, nếu bạn muốn bảo vệ 1 đoạn code với rất nhiều hoạt động không đồng bộ bằng 1 khối try-catch lớn, thì nó sẽ không có tác dụng nhiều:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
try {
db.User.get(userId, function(err, user) {
if(err) {
throw err
}
// ...
usernameSlug = slugifyUsername(user.username)
// ...
})
} catch(e) {
console.log(‘Oh no!’)
}
|
Nếu callback đến “db.User.get” bị loại không đồng bộ thì scope chứa khối try-catch sẽ không còn phù hợp để call vẫn có thể bắt các lỗi từ trong callback.
Đây là cách giải quyết các lỗi khác nhau trong Node.js, đóng vai trò quan trọng để theo dõi pattern (err, …) trên tất cả các đối số hàm callback – đối số đầu tiên của các callbacks xuất hiện nhiều khả năng sẽ bị lỗi.
Numbers trong JavaScript là các floating points – không có dữ liệu kiểu số tự nhiên. Vì bạn sẽ không thường gặp phải các numbers đủ lớn để gây áp lực đến các limits của float nên có thể bạn sẽ không thấy đây là vấn đề. Đó chính là thời điểm những lỗi liên quan xảy ra. Vì các floating point numbers (số thực dấu phấy động) chỉ có thể giữ các integer representations (phép kiểu số tự nhiên) ở 1 giá trị nào đó, nên nếu vươt quá giá trị đó trong bất kì phép tính nào cũng sẽ khiến giá trị đó trở nên lộn xộn. Tuy có vẻ kì lạ, nhưng dòng code dưới đây đã được xác định là đúng trong Node.js:
1
2
3
|
Math.pow(2, 53)+1 === Math.pow(2, 53)
|
Thật không may, những điểm mập mờ về number trong JavaScript vẫn chưa kết thúc tại đây. Dù các Numbers là những floating points (đại số dấu phẩy động), các operators chạy trong integer data types (kiểu dữ liệu số tự nhiên) cũng chạy ở đây:
1
2
3
4
|
5 % 2 === 1 // true
5 >> 1 === 2 // true
|
Tuy nhiên, khác với các toán tử số học, bitwise operators (phép toán thao tác bit) và shift operators (các phép toán shift) chỉ chạy trong đuôi 32 bít của các number “integer” lớn. Ví dụ, cố gắng shift “Math.pow(2, 53)” bằng 1 sẽ luôn xác định bằng 0. Cố gắng thực hiện thao tác bit 1 với number cùng độ lớn sẽ xác định bằng 1.
1
2
3
4
5
|
Math.pow(2, 53) / 2 === Math.pow(2, 52) // true
Math.pow(2, 53) >> 1 === 0 // true
Math.pow(2, 53) | 1 === 1 // true
|
Có thể bạn hiếm khi phải giải quyết các numbers lớn, nhưng nếu bạn phải giải quyết chúng, sẽ có rất nhiều thư viện integer lớn hỗ trợ thực hiện các thao tác số học quan trọng trên các numbers có độ chính xác lớn, như node-bigint.
Giả sử bạn muốn xây dựng 1 web server như proxy nhỏ, phản hồi các requests bằng cách lấy content từ 1 web server khác. Như ví dụ bên dưới, chúng ta phải xây dựng 1 web server hiển thị các hình ảnh Gravatar:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
var http = require('http')
var crypto = require('crypto')
http.createServer()
.on('request', function(req, res) {
var email = req.url.substr(req.url.lastIndexOf('/')+1)
if(!email) {
res.writeHead(404)
return res.end()
}
var buf = new Buffer(1024*1024)
http.get('http://www.gravatar.com/avatar/'+crypto.createHash('md5').update(email).digest('hex'), function(resp) {
var size = 0
resp.on('data', function(chunk) {
chunk.copy(buf, size)
size += chunk.length
})
.on('end', function() {
res.write(buf.slice(0, size))
res.end()
})
})
})
.listen(8080)
|
Trong ví dụ về 1 vấn đề của Nodejs, chúng ta đang lấy hình ảnh từ Gravatar, thêm vào 1 Buffer, sau đó phản hồi lại request. Vì các hình ảnh Gravatar không quá lớn nên việc này cũng không quá tệ. Nhưng, nếu kích cỡ của các contents mà chúng ta đang proxy là hàng ngàn Megabytes thì lại là chuyện khác.
Cách khác tốt hơn sẽ như sau:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
http.createServer()
.on('request', function(req, res) {
var email = req.url.substr(req.url.lastIndexOf('/')+1)
if(!email) {
res.writeHead(404)
return res.end()
}
http.get('http://www.gravatar.com/avatar/'+crypto.createHash('md5').update(email).digest('hex'), function(resp) {
resp.pipe(res)
})
})
.listen(8080)
|
Ở đây, chúng ta lấy hình ảnh và trả phản hồi lại cho client, mà không cần đọc toàn bộ content trong buffer trước khi thực hiện nó.
Trong Node.js, “console.log” cho phép bạn print hầu hết mọi thứ trong console (giao diện điều khiển). Chuyển 1 đối tượng vào đó và nó sẽ print thành 1 đối tượng Javascript bằng chữ. Nó chấp nhận các đối số ngẫu nhiên bất kì và print chúng tại các vùng tách biệt. Có rất nhiều lý do tại sao 1 lập trình viên cảm thấy bị cám dỗ phải sử dụng console.org để debug code của mình, nhưng tôi khuyến khích bạn nên tránh dùng “console.org” trong code thật. Bạn không nên viết “console.log” xuyên suốt các code của mình để debug và sau đó xóa chúng khi không cần nữa. Thay vào đó, sử dụng 1 trong những libraries hay được xây dựng để làm riêng việc này, như debug.
Các packages như trên sẽ là những cách tiện lợi để kích hoạt và vô hiệu vài dòng debug nào đó khi bạn bắt đầu ứng dụng. Ví dụ, với debug, bạn có thể thiết lập môi trường DEBUG biến thể để ngăn dòng debut bất kì không bị print đến terminal.
1
2
3
4
5
|
// app.js
var debug = require(‘debug’)(‘app’)
debug(’Hello, %s!’, ‘world’)
|
Để kích hoạt các debug lines, chỉ cần chạy code này với môi trường DEBUG biến thể được thiết lập với “app” hoặc “*”:
1
2
3
|
DEBUG=app node app.js
|
Bất kể code Node.js của bạn đang chạy trong production hoặc trong môi trường lập trình nội bộ, thì 1 chương trình supervisor quản lý code là công cụ hữu ích để tổ chức, dàn xếp chương trình của bạn. Một practice thường được các developers khuyến dùng để thiết kế và hoàn thiện các ứng dụng hiện đại sẽ cho thấy code của bạn phải thất bại sớm. Nếu một lỗi bất ngờ xảy ra, đừng cố gắng giải quyết nó, hãy để chương trình của bạn crash và sử dụng supervisor để khởi động lại trong vài giây.
Lợi ích của các chương trình supervisor không chỉ là việc khởi động lại các chương trình bị crash. Những công cụ này cho phép bạn khởi động chương trình lúc crash, cũng như khởi động lại chúng khi 1 số files thay đổi. Nhờ đó, các chương trình lập trình Node.js đem đến trải nghiệm tốt hơn.
Hiện nay có rất nhiều chương trình supervisor dành cho Node.js như
Tất cả các công cụ này đều có điểm tốt và điểm không tốt. Một số công cụ phù hợp để quản lý nhiều ứng dụng trong cùng 1 thiết bị, trong khi các công cụ khác hỗ trợ quản lý log tốt hơn. Tuy nhiên, nếu bạn muốn bắt đầu với 1 chương trình như thế, tất cả các công cụ đều ổn.
Một vài vấn đề của Node.js có thể sẽ ảnh hưởng tồi tệ đến chương trình của bạn. Một số là nguyên nhân khiến bạn bực mình khi bạn đang cố gắng hoàn thiện những thứ đơn giản nhất trong Node.js. Mặc dù Node.js đã hỗ trợ rất nhiều cho những người mới bắt đầu, những vẫn có vài chỗ bị rối. Các lập trình viên từ các ngôn ngữ lập trình khác có thể gặp vài vấn đề trên hoặc một số vấn đề phổ biến trong các developer Node.js mới.
May mắn là, bạn có thể dễ dàng tránh được.
Tôi hy vọng hướng dẫn ngắn này sẽ giúp những người mới bắt đầu viết code Node.js tốt hơn và phát triển phần mềm ổn định và tiện ích hơn.
Via Techtalk.vn