NodeJs Event: ý nghĩa và cách sử dụng

Giang Coffee
4 min readAug 23, 2019

--

Như đã nói qua ở trong bài NodeJs là gì và bài Event Loop trong NodeJs thì chúng ta đã biết NodeJs sử dụng cơ chế Event-Driven với Single Thread để xử lý nhiều request một lúc hơn cách bình thường. Chính vì thế không có lí do gì mà ta lại không tận dụng cơ chế Event này nhằm tăng hiệu năng hệ thống và tạo sự linh hoạt cho lập trình viên.

Ở các bài trước chúng ta đang dừng lại ở mức xây dựng hệ thống các rest api sử dụng jwt làm cơ chế xác thực và cấp quyền truy cập. Ở bài này chúng ta sẽ bàn về ứng dụng của event trong một dự án rest api. Vậy thì event là gì và ứng dụng của nó như thế nào ?

Event đơn giản nó là một sự kiện được sinh ra tại một thời điểm với một điều kiện được định nghĩa trước. Ví dụ sự kiện click chuột được sinh ra khi người dùng click chuột trái, sự kiện gõ bàn phím được sinh ra khi người dùng thao tác với bàn phím trên một input text … Còn rất nhiều sự kiện khác có thể kể ra.

Có hai nhân tố liên quan trực tiếp đến một event đó là EmiterReiceiver (Listener). Nếu một Event được sinh ra (emit) mà không có ai nhận được (receive) thì không có chuyện gì xảy ra. Hoặc nếu một hàm lắng nghe (listen) một Event mà Event đó không được sinh ra thì cũng không có gì thay đổi. Vì thế một vòng đời đầy đủ của Event sẽ là :

Emiter -> Event -> Listener

Để hiểu được tác dụng của Event ta giả sử một ứng dụng có 2 trang: trang danh sách user và trang thống kê tổng số user và ta đang ở trang danh sách. Khi tạo mới một user thì các bước thông thường sẽ là :

  • (1) user điền thông tin
  • (2) user submit
  • (3) tạo mới user, chèn thêm một record vào DB
  • (4) tính toán lại tổng số user và lưu vào bảng report
  • (5) trả lại response cho user
  • (6) client nhận response
  • (7) client thêm một dòng vào danh sách user

Vấn đề là bước số 4 có thực sự cần làm ngay ? Ví dụ nếu bước số 4 cần 3s để hoàn thành, nếu ta để bước số 4 làm sau thì ta user sẽ không phải chờ thêm 3s, đối với user thì ứng dụng chạy nhanh hơn 3s vì user không hề biết các bước thực hiện gồm những gì. Trên trang danh sách user rõ ràng ta không cần thông tin về tổng số user, nó chỉ cần thiết khi ta vào trang thống kê báo cáo.

Dĩ nhiên ta không bỏ bước số 4 mà là chỉ đẩy nó ra ngoài luồng xử lý ở trên. Nếu các bạn còn nhớ về Event-Loop thì các Event sẽ được đẩy vào một hàng đợi và sẽ được xử lý ngay khi CPU rảnh. Như vậy thì trong khi user chuyển sang trang thống kê báo cáo thì tổng số user đã được tính toán lại và sẵn sàng cho việc hiển thị.

Ta sẽ sử dụng thư viện EventEmiter có sẵn của NodeJs để gửi và nhận Event. Tạo thư mục events trong thư mục src ta sẽ có cây thư mục như sau :

restify_part4/
|__src/
| |_models/
| |_controllers/
| |_events
| |_repositories/
| |_routes/
| |_services
|
|__index.js
|__package.json

Tạo file dispatcher.js trong thư mục events, đây là nơi sinh ra tất cả các event :

"use strict";

const events = require('events');
const eventEmitter = new events.EventEmitter();
const UserListener = require('./listeners/userListener');

function newUserEvent(name, email) {
//lắng nghe sự kiện
eventEmitter.once('NEW_USER_CREATED', UserListener.newUserListener);
// sinh ra sự kiện
eventEmitter.emit('NEW_USER_CREATED', name, email);
}

module.exports = {
newUserEvent: newUserEvent,
}

Tên event ở đây phải là duy nhất nếu không hàm nào listen trước sẽ được gọi trước gây ra các hiệu ứng không mong muôn. Tốt nhất tên các event nên được tạo riêng ở một file constant và được bắt đầu bằng tên công ty hay tên dự án. ví dụ : REST_API_NEW_USER_CREATED hoặc TEST_NEW_USER_CREATED

Tạo thư mục listeners trong thư mục events, đây sẽ là nơi chứa tất cả các listener của chúng ta. Ở file dispatcher ở trên ta có import UserListener, dó đó ta cần tạo file userListener.js trong thư mục listeners với nội dung :

function newUserListener(name, email) {
// tính toán tổng số user ở đây
console.log('New user has been created with name: ' + name + ' and email: ' + email);
}

module.exports = {
newUserListener: newUserListener,
};

Như vậy mỗi khi có một user đăng ký mới ta chỉ việc “bắn” ra event NEW_USER_CREATED là tổng số user sẽ được tự động tính toán lại và cập nhật vào DB. Sửa file userController.jsnhư sau

const Dispatcher = require('../events/dispatcher');

/**
* @param req
* @param res
* @param next
*/
function create(req, res, next) {
let data = req.body || {};

if (!data.password || !data.email) {
return next(new errors.InvalidContentError('"password" và "email" không được để trống!'));
}

//encrypt plain password
data.password = HashService.saltHashPassword(data.password);
UserRepository.save(data)
.then(function (user) {
Dispatcher.newUserEvent(data.name, data.email);
res.send(201, user);
next();
})
.catch(function (error) {
console.error(error);
return next(error);
})
.done();
}

Toàn bộ source code của phần này được lưu tại đây

--

--