Bài trước: Web back-end (13) - Lập trình client-server
Như bạn đã trải
nghiệm ở các bài học trước, quan sát mã nguồn của index.js, bạn sẽ thấy: hàm
app.listen() luôn được viết ở cuối tập tin, và mặc dù viết ở cuối tập tin,
nhưng dường như nó lại luôn luôn được chạy trước? Nó chạy trước thì mới lắng
nghe được các request từ client, để gọi các hàm xử lý tương ứng? Các hàm xử lý
request lại được viết ở phía trên hàm app.listen().
Bạn đã thấy:
logic xử lý của chương trình không phải luôn luôn thực thi tuần tự từ trên
xuống dưới?
Để giải đáp hiện tượng trên, chúng ta cùng tìm hiểu một số chủ đề liên quan.
14.1 Lập trình hướng sự kiện
Lập trình hướng
sự kiện (event-driven) là một mô hình lập trình mà luồng thực thi của chương
trình được quyết định bởi các sự kiện (events). Các sự kiện này có thể là:
- Hành động của
người dùng (nhấp chuột, gõ phím).
- Các thông điệp
từ các chương trình khác.
- Các sự kiện do
hệ thống tạo ra (nhận được request, thao tác đọc/ghi tập tin).
Thay vì chạy theo thứ tự mã nguồn từ trên xuống dưới, chương trình sẽ chờ đợi các sự kiện xảy ra và thực thi các hàm xử lý sự kiện (event handler) tương ứng.
14.2 Lập trình hướng sự kiện trong Nodejs
Nodejs là một môi
trường được xây dựng dựa trên mô hình lập trình hướng sự kiện. Đây là một trong
những đặc điểm cốt lõi giúp Node.js hoạt động hiệu quả và có khả năng xử lý
nhiều kết nối đồng thời, đặc biệt phù hợp với các ứng dụng thời gian thực, các
ứng dụng chuyên về I/O như ứng dụng web, API, hoặc ứng dụng mạng.
Ứng dụng chuyên
về I/O (I/O-intensive application) là các ứng dụng mà phần lớn thời gian xử lý
hoặc tài nguyên được dành cho các hoạt động nhập/xuất (Input/Output), tức là
các tác vụ liên quan đến việc đọc/ghi dữ liệu từ hoặc đến các nguồn bên ngoài
như tập tin, cơ sở dữ liệu, mạng, hoặc thiết bị phần cứng, thay vì tập trung
nhiều vào tính toán (CPU-intensive).
Các thành
phần của Mô hình lập trình hướng sự kiện
Mô hình lập trình
hướng sự kiện trong Nodejs gồm các thành phần:
- Event: sự kiện
- Event Emitter:
bộ quản lý sự kiện
- Event loop:
vòng lặp sự kiện
- Event handler:
hàm xử lý sự kiện
Xem hình minh
họa:
Mô tả cách hoạt
động của mô hình:
[1] EventEmitter
Trong hệ thống sẽ
có một thành phần điều khiển, nó quản lý việc phát ra sự kiện của các đối tượng
và gửi sự kiện tới nơi cần nghe (các đối tượng đang lắng nghe sự kiện tương
ứng). Thành phần này được gọi là EventEmitter - Bộ quản lý sự kiện).
Các phương thức
chính của EventEmitter:
- on(eventName, listener): khai báo một tên sự kiện
(eventName) sẽ phát ra, và đăng ký một hàm lắng nghe và xử lý đi kèm.
- emit(eventName, [arguments...]): phát ra một sự kiện, có
tên là eventName, và các tham số đi kèm (nếu có).
[2] Event
Event là một hành
động hoặc sự thay đổi trạng thái xảy ra trong ứng dụng, gọi chung là sự kiện
(Event). Ví dụ:
- Yêu cầu HTTP
đến máy chủ (GET request, POST request).
- Hoàn thành việc
đọc/ghi tập tin.
- Hết thời gian
chờ (timeout).
- Sự kiện lỗi.
[3] Event Loop
(Vòng lặp sự kiện)
- Event Loop là cơ chế cốt lõi
của Nodejs, cho phép nó xử lý các sự kiện không đồng bộ một cách hiệu quả.
- Event Loop liên
tục kiểm tra hàng đợi sự kiện (event queue) và thực thi các hàm callback khi có
sự kiện xảy ra.
- Điều này cho
phép Nodejs xử lý nhiều yêu cầu đồng thời mà không bị chặn (blocking).
[4] Event Handler
(Hàm xử lý sự kiện)
- Event handler
là một hàm được gọi khi một sự kiện xảy ra.
- Event handler thường được đăng ký với một EventEmitter bằng phương thức on().
14.3 Thực hành lập trình hướng sự kiện
Chúng ta cùng
thực hành để làm quen với lập trình hướng sự kiện.
Chúng ta sẽ viết
một chương trình đơn giản như sau:
- Sử dụng module events của Nodejs để lập trình
hướng sự kiện
- Tạo một bộ quản
lý sự kiện (EventEmitter)
- Sử dụng
EventEmitter để khai báo một sự kiện, và đăng ký hàm xử lý đi kèm
- Phát sinh sự
kiện (đã khai báo), để kiểm tra hoạt động của các thành phần
Để cho dễ hiểu,
chúng ta sẽ viết chương trình theo 2 cách: [1] viết theo kiểu hàm thông thường
(cách này viết để hiểu cơ chế làm việc) và [2] viết theo kiểu hàm mũi tên
(arrow function) (cách này viết theo kiểu lập trình trong ứng dụng thực tế).
[1] Viết
theo kiểu hàm thông thường
[index.js]
'use strict'
const express = require('express')
const app = express();
const port = process.env.PORT || 9000
// gọi module events
const events = require('events');
// tạo ra một EventEmitter
const eventEmitter = new events.EventEmitter();
// đăng ký tên sự kiện, gắn với
hàm lắng nghe và
// xử lý sự kiện tương ứng -
xuLythongBao là event handler
eventEmitter.on('hetGio', xuLyThongBao);
// định nghĩa hàm xuLyThongBao
function xuLyThongBao(tb) {
console.log(tb);
}
// phát ra sự kiện, kèm theo
thông điệp
setTimeout(()=> {
eventEmitter.emit('hetGio','Hết giờ học rồi, về thôi!!!!!!!')
}, 2000);
// khoi dong web server
app.listen(port, () => {
console.log(`server dang chay tren cong ${port}`);
});
[2] Viết
theo kiểu hàm mũi tên
[index.js]
'use strict'
const express = require('express')
const app = express();
const port = process.env.PORT || 9000
// gọi module events
const events = require('events');
// tạo ra một EventEmitter
const eventEmitter = new events.EventEmitter();
// đăng ký tên sự kiện, gắn với
hàm lắng nghe và
// xử lý sự kiện tương ứng
eventEmitter.on('hetGio', (thongBao) => {
console.log(thongBao);
});
// phát ra sự kiện, kèm theo
thông điệp
setTimeout(()=> {
eventEmitter.emit('hetGio','Hết giờ học rồi, về thôi!!!!!!!')
}, 2000);
// khoi dong web server
app.listen(port, () => {
console.log(`server dang chay tren cong ${port}`);
});
Kết quả: khi
chạy, sau 2 giây, cửa sổ console trên web server sẽ có dòng thông báo sau
server dang chay tren cong 9000
Hết giờ học rồi, về thôi!!!!!!!
14.4 Bài tập
Bài tập 14.1 Viết
lại các đoạn mã trong bài học.
Bài tập 14.2 Sử
dụng module events của Nodejs để lập trình
hướng sự kiện.
Viết một chương trình đơn giản như sau:
- Khai báo sự
kiện vaoLop (vào lớp), với hàm xử lý đi kèm là xuLyVaoLop
- Sử dụng hàm
setTimeOut() để hẹn giờ vào lớp là 5 giây
- Hết 5 giây, web
server sẽ thông báo “Đã đến giờ học” tới cửa sổ console của web server
- Viết bằng 2
cách: hàm thông thường và hàm mũi tên.
Câu hỏi 14.3 Mô hình lập trình hướng sự kiện trong Node.js là gì?
Phát biểu nào sau đây không đúng?
A. Event Emitter
liên tục kiểm tra hàng đợi sự kiện và thực thi các hàm callback khi có sự kiện
xảy ra.
B. Event là các
hành động hoặc sự thay đổi trạng thái xảy ra trong ứng dụng.
C. Event Loop
liên tục kiểm tra hàng đợi sự kiện và thực thi các hàm callback khi có sự kiện
xảy ra.
D. Event Handler là các hàm được gọi khi một sự kiện xảy ra.