## Khái niệm Nodejs
Nodejs là một nền tảng (Platform) phát triển độc lập được xây dựng
ở trên Javascript Runtime của Chrome mà chúng ta có thể xây dựng được
các ứng dụng mạng một cách nhanh chóng và dễ dàng mở rộng.
Phần Core bên dưới của Nodejs được viết hầu hết bằng C++
nên cho tốc độ xử lý và hiệu năng khá cao.
Nodejs tạo ra được các ứng dụng có tốc độ xử lý nhanh, realtime thời gian thực.
## Những ứng dụng nên viết bằng Nodejs
1. Websocket server: Các máy chủ web socket như là Online Chat, Game Server…
2. Fast File Upload Client: là các chương trình upload file tốc độ cao.
3. Ad Server: Các máy chủ quảng cáo.
4. Cloud Services: Các dịch vụ đám mây.
5. RESTful API: đây là những ứng dụng mà được sử dụng cho các ứng dụng khác thông qua API.
6. Any Real-time Data Application:
bất kỳ một ứng dụng nào có yêu cầu về tốc độ thời gian thực.
Micro Services: Ý tưởng của micro services là chia nhỏ một ứng dụng lớn thành
các dịch vụ nhỏ và kết nối chúng lại với nhau. Nodejs có thể làm tốt điều này.
## Chú ý
1. Các ứng dụng Nodejs được viết bằng javascript,
ngôn ngữ này là một ngôn ngữ khá thông dụng.
2. Nodejs chạy đa nền tảng phía Server, sử dụng kiến trúc hướng sự kiện Event-driven,
cơ chế non-blocking I/O làm cho nó nhẹ và hiệu quả.
3. Có thể chạy ứng dụng Nodejs ở bất kỳ đâu trên máy Mac – Window – Linux,
hơn nữa cộng đồng Nodejs rất lớn và hoàn toàn miễn phí.
4. Các ứng dụng NodeJS đáp ứng tốt thời gian thực và chạy đa nền tảng, đa thiết bị.
5. Xử lý đơn luồng (single thread)
## Cài đặt
https://nodejs.org/en/download/
## Sử dụng cơ bản
Tạo mới file `package.json`: `npm init`
Install thư viện http: `npm i http`
Tạo mới file index.js:
<!-->
// Import thư viện HTTP (HTTP Module)
var http = require("http");
// Khởi tạo 1 máy chủ HTTP với port 8888
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World!");
response.end();
}).listen(8888);
<-->
==>
chạy command: `node index.js` để tạo mới 1 máy chủ HTTP
và sau đó vào 1 browser bất kì mở http://localhost:8888/ để xem kết quả.
- Tạo máy chủ HTTP lắng nghe ở cổng 8888, tuy nhiên không thực thi gì:
<!-->
var http = require("http");
var server = http.createServer();
server.listen(8888);
<-->
## sử dụng hàm
function say(word) {
console.log(word);
}
function execute(someFunction, value) {
// đưa biến value vào function `someFunction` để xử lý
someFunction(value);
}
execute(say, "Hello");
- Anonymous function
function execute(someFunction, value) {
someFunction(value);
}
execute(function(word){ console.log(word) }, "Hello");
## Cơ chế xử lý request
var result = database.query("SELECT * FROM hugetable");
console.log("Hello World");
Giả sử thao tác đến database diễn ra rất chậm, có rất nhiều bản ghi thoả mãn yêu cầu,
thời gian thực thi có thể mất đến vài giây.
Trong mô hình thực thi của PHP thì đó không phải là vấn đề đáng quan tâm:
`máy chủ web PHP khởi tạo một process riêng cho mỗi request nó nhận được`.
Nếu một trong những request này chậm hơn bình thường,
nó chỉ có thể ảnh hưởng đến thời gian tải trang của người tạo ra request đó,
chứ không gây ảnh hưởng đến những người dùng khác.
Mô hình thực hiện của Node.js không giống như vậy - nó chỉ dùng duy nhất một process.
Nếu có truy vấn tới database nào đó tốn nhiều thời gian, nó sẽ làm chậm toàn bộ process
- mọi thứ sẽ bị dừng lại cho đến khi truy vấn kia kết thúc.
Để tránh tình trạng này xảy ra,
JavaScript và Node.js đưa ra khái niệm "dựa theo sự kiện" (event-driven),
gọi ngược không đồng bộ (asynchronous callback),
bằng cách sử dụng một "vòng lặp sự kiện" (event loop).
- Cải tiến với asynchronous library
// truy vấn tới database một cách không đồng bộ
database.query("SELECT * FROM hugetable", function(rows) {
var result = rows;
});
console.log("Hello World");
Thay vì phải chờ xử lý gọi DB như trước, với cải tiến sử dụng bất đồng bộ này,
Node JS gửi truy vấn đến DB và ghi nhận lại là đang chờ xử lý sau đó sẽ xử lý tiếp cái khác.
Đến một thời điểm nào đó trong tương lai - khi truy vấn kết thúc, kết quả được trả về
- nó sẽ phải thực hiện những gì được viết trong hàm vô danh.
Ở phần code trên. Dòng chữ "Hello World" được in ra ngay lập tức trên console.
Sau đó bắt đầu một vòng lặp vô tận, và cứ thế chờ đợi,
không xử lý bất kỳ gì khác cho đến khi có một sự kiện nào đó đánh thức nó,
ví dụ như truy vấn tới database đã có dữ liệu trả về.
Mô hình thực thi không đồng bộ, đơn luồng,
và dựa trên sự kiện này không phải thứ gì đó hoàn hảo tuyệt đối.
Nó chỉ là một trong nhiều mô hình đang tồn tại, nó cũng có những nhược điểm,
một trong số đó chính là: nó chỉ có thể chạy trên một nhân của CPU mà thôi.
var http = require("http");
// Khi một request đến server. hàm onRequest sẽ được gọi
function onRequest(request, response) {
console.log("Request received.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
==>
Chạy `node server.js` sẽ thấy ở màn hình console in ra `Server has started.`
Và mỗi một request đến server, màn hình console sẽ in ra `Request received.`
## Xử lý đơn luồng (single thread)
Máy ảo và OS sẽ chạy I/O song song và khi trở lại Javascript code,
phần Javascript code sẽ chỉ chạy đơn luồng.
Nói cách khác, tất cả mọi thứ chạy song song, ngoại trừ Javascript code,
Nếu tất cả những gì ta làm là asynchronous I/O.
// call asynchronous to get data in DB
db.findOne('SELECT ... LIMIT 1', function(err, result) {
if (err) return console.error(err)
console.log(result)
})
console.log('Running query')
setTimeout(function() {
console.log('Hey there')
}, 1000)
Có thể lệnh query đến database sẽ tốn hơn một phút nhưng dòng chữ "Running query"
sẽ xuất hiện ngay sau khi tạo lệnh query.
Và ta sẽ thấy dòng chữ "Hey there" sau một giây sau khi tạo lệnh query dù query
có đang chạy hay không.
Nodejs app chỉ gọi function mà không block việc thực thi của các phần code khác.
Nó sẽ được thông báo thông qua function callback khi lệnh query chạy xong
và ta sẽ thu được kết quả.
## các tác vụ chiếm dụng CPU
db.findAll('SELECT ...', function(err, results) {
if (err) return console.error(err)
// Heavy computation and many results
for (const encrypted of results) {
const plainText = decrypt(encrypted)
console.log(plainText)
}
})
Trong trường hợp này, chúng ta có quá nhiều kết quả và cần phải tính toán phức tạp,
nặng nề trên các kết quả đó.
Điều này có thể mất vài giây, và trong thời gian này việc thực thi
các phần Javascript khác sẽ bị queue,
điều đó có nghĩa ta sẽ block tất cả các user trong thời gian này
nếu ta đang chạy một server trên cùng một app.
==>
- Cách 1: Sử dụng setImmediate(callback) để đưa function xử lý vào lại hàng đợi
function processChunk() {
if (arr.length === 0) {
// code that runs after the whole array is executed
}
else {
console.log('processing chunk');
// pick 10 items and remove them from the array
const subarr = arr.splice(0, 10)
for (const item of subarr) {
// do heavy stuff for each item on the array
doHeavyStuff(item)
}
// Put the function back in the queue
setImmediate(processChunk)
}
}
processChunk()
Bây giờ ta sẽ chỉ xử lý mỗi lần 10 item và gọi setImmediate(callback)
vì vậy nếu có việc gì khác mà chương trình cần phải làm,
nó sẽ được thực hiện chen giữa các khối 10 item.
- Cách 2: trigger một node cron jobs
## Tạo một custom module
Biến một đoạn code thành một module có nghĩa là chúng ta phải trích xuất (`export`)
một phần chức năng mà chúng ta muốn
(từ một file nào đó, hay cả file) để đưa vào module kia.
- Tạo module server
var http = require("http");
function start() {
function onRequest(request, response) {
console.log("Request received.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
- Sử dụng module
Tạo mới file index.js với nội dung sau:
var server = require("./server");
server.start();
- Ta có thể export ra một const, functions hay class
module.exports = {
groupCloudFunctions: {
mobile: buyerGroupCloudFunction,
web: operatorGroupCloudFunction,
},
allCloudFunctionNames,
hasCloudFunction: (fnName) => allCloudFunctionNames.includes(fnName),
}
- Export một object
const InitData = require("./initData")
const UpdateOrderAmountShopAndItem = require("./updateOrderAmountShopAndItem")
...
module.exports = Object.assign({
// createDefaultCategories: createCategories,
InitData,
UpdateOrderAmountShopAndItem,
UpdateItemsStatus,
AddIndexSchema,
DeleteImage,
})
## Các thành phần của Node JS
- Modules, Error Handling
- Debugger, Streaming, DNS, Domain, Global, Net
- Console, Cluster, Add-ons, Buffer, Callbacks, Crypto
## If else
if (time < 10) {
greeting = "Good morning";
} else if (time < 20) {
greeting = "Good day";
} else {
greeting = "Good evening";
}
## switch case
switch(expression) {
case x:
// code block
break;
case y:
// code block
break;
default:
// code block
}
## for
var array = ['C++', 'Java', 'JavaScript', 'NodeJS'];
for(var i = 0; i < array.length; i++){
console.log(array[i]);
}
- For .. of:
var results = [1,2];
for (let item of results) {
console.log(item);
}
- for .. of with key and value:
const object1 = {a: 'somestring', b: 42};
for (const [key, value] of Object.entries(object1)) {
console.log(`${key}: ${value}`);
}
- for .. in:
const object2 = { a: 1, b: 2, c: 3 };
for (const property in object) {
console.log(`${property}: ${object[property]}`);
}
## While
while (condition) {
// execute code as long as condition is true
}
## do .. while
do {
do_work();
} while (condition);
## For each
- for each a list string
const arr = ['cat', 'dog', 'fish'];
arr.forEach(element => {
console.log(element);
});
- for each a list number
const arr = [1, 2, 3, 8, 7];
arr.forEach(element => {
console.log(element);
});
- for each with key and value
var reversed = fields.slice().reverse()
reversed.forEach(function (item, index) {
// ...
});
- For each with `lodash`/forEach Module
const _ = require('lodash')
// OR const _forEach = require("lodash/forEach")
const processItems = async (orderItems, isOperator = false) => {
let itemIds = Helper.extractToArray(orderItems, 'id')
let items = await ItemModel.getItemListByItemIds(itemIds, undefined, true, isOperator)
_.forEach(items, (item, index) => {
orderItems[index].images = item.images
orderItems[index].slug = item.slug || ""
orderItems[index].shopSlug = item.shop.slug || ""
delete orderItems[index].image
})
return orderItems
}
## 2. For - array with object element
var arrayB = [1,2,3,{"fruit":"orange", "drink":"beer"}];
document.write("2. Array: " + arrayB.join());
for (var j=0; j < arrayB.length; j++) {
if (typeof arrayB[j] == "object") {
var obj = arrayB[j];
for (var prop in obj) {
// skip loop if the property is from prototype
if(!obj.hasOwnProperty(prop)) continue;
// output key and object value
document.write(prop + " = " + obj[prop]);
}
}
else {
document.write(j.toString() + ":" + arrayB[j]);
}
}
## 3. multi-dimensional arrays
var arr = [['a', 'b'], ['c','d']];
for (var arrayIndex in arr) {
if (Array.isArray(arr[arrayIndex])) {
var arrTmp = arr[arrayIndex];
for (var z=0; z<arrTmp.length; z++) {
document.write(z.toString() + ":" + arrTmp[z]);
}
}
// document.write(arr[arrayIndex][0] + ' ' + arr[arrayIndex][1]);
}
## 4. Normal Object
var validation_messages = {
"key_1": {"your_name": "jimmy", "your_msg": "hello world"},
"key_2": {"your_name": "billy", "your_msg": "foo equals bar"}
}
for (var index in validation_messages) {
for (const [key, value] of Object.entries(validation_messages[index])) {
console.log("key: "+key+". value:"+value);
}
}
## 5. Process JSON Data
var objJSON = JSON.parse('{ "name":"Krunal", "age":25, "city":"Rajkot", "degree": "BE"}');
for (var prop in objJSON) {
console.log(prop + " = " + objJSON[prop]);
}
for (const [key, value] of Object.entries(objJSON)) {
console.log(key + " = " + value);
}
Không có nhận xét nào:
Đăng nhận xét