Nội dung bài viết Lập trình đồng bộ hay bất đồng bộ không đơn giản như những gì được viết trong nhiều blog, đó chỉ là lý
thuyết, vậy làm sao chúng ta biết được hiệu suất của nó thế nào đây? Lưu ý: Đây là một trong những bài viết trong Serires Nodejs: Học 7 ngày Là một lập trình viên bất cứ level nào bạn cũng hiểu về khái niệm lập trình đồng bộ và bất đồng bộ. Chúng ta chỉ hiểu nôm na rằng nếu lâp trình không đồng bộ thì sử dụng và thực hiện với sự trợ giúp của
callbacks, Promise, async và await. Và hơn ai hết cả thảy chúng ta đều biết rằng không đồng bộ sẽ tốt hợn rất nhiều, vậy nó thực sự tốt như thế nào? Tôi sẽ làm một ví dụ giúp bạn hiểu rõ thực sự. Và cũng là để cho các bạn có
thêm kiến thức nhằm có điểm công trong mắt nhà tuyển dụng vị trí back-end nodejs. Để so sánh hiệu suất giữa đồng bộ và bất đồng bộ, thì chúng tôi sẽ sử dụng package bcrypt và expressjs. Vì bcrypt có cung cấp các phương thức đồng bộ và không đồng bộ để băm một chuỗi nhất định. lấy nó làm ví dụ qủa là hợp lý. Ở đây không giải thích nhiều về 2 khái niệm Async và
Sync chỉ tập trung test hiệu suất. OK!! Đầu tiên hãy install các packages: ## Tạo file app.js Cần chú ý đoạn code trên, nếu bạn đủ trình độ thì bỏ qua khúc này, còn chưa hãy để tôi giải thích cho bạn. Chúng ta có 2 nhiệm vụ, đó là gọi hai routes là Apache bench là một công cụ để đánh giá các máy chủ HTTP. Sủ dụng lệnh sau đây để test về đồng bộVideo học lập trình mỗi ngày
So sánh hiệu suất Async và Sync
AnonyStick$ npm i bcrypt express --save
added 39 packages, and audited 589 packages in 4s
5 packages are looking for funding
run `npm fund` for details
2 high severity vulnerabilities
Some issues need review, and may require choosing
a different dependency.
Run `npm audit` for details.
const express = require['express']
const bcrypt = require['bcrypt']
const app = express[]
app.get['/sync', [req, res] => {
let hashed = bcrypt.hashSync['secret', 10]
return res.send[hashed]
}]
app.get['/async', async [req, res] => {
let hashed = await bcrypt.hash['secret', 10]
return res.send[hashed]
}]
app.listen[3000, [] => console.log['Server started on port 3000']]
/sync
và /async
. bcrypt.hashSync[] là một method đồng bộ, và bcrypt.hash[] không đồng bộ. Bây giờ chúng ta sẽ
test hiệu suất hai routes này thông qua apache bench.Sync mode benchmark
AnonyStick$ ab -k -c 20 -n 250 "//localhost:3000/sync"
và
AnonyStick$ ab -k -c 20 -n 250 "//localhost:3000/async"
Nếu vẫn chưa biết về apache bench thì hãy để tôt giải thích tiếp:
- ab: viết tắt của apache bench
- -k: Kích hoạt tính năng HTTP KeepAlive
- -c: Số lượng nhiều yêu cầu thực hiện cùng một lúc.
- -n: Số lượng yêu cầu thực hiện cho phiên đo điểm chuẩn
Kết quả cho câu lênh trên được đưa ra dưới đây. Hay để ý hình ảnh chi tiết một chút
SO SÁNH: Trước tiên tôi đã tổng kết cho bạn đọc nhanh nhất:
Xem hình ảnh chúng ta có thể thấy rằng. Cơ chế async tốt hơn sync về tính trung bình xử lý đồng thời. 1446.324 so với 384.406. Và còn nhiều thông số khác nhỉnh hơn. Nếu bạn cảm thấy chưa thoả mãn, vui lòng đọc thêm thông tin về các chỉ số trên đó.
Tóm lại
So sánh để làm gì? Để luôn nhớ rằng sử dụng chế độ async khi mã của bạn cần thực hiện một số thao tác Blocking i/o vì nó không chặn Event Loop.
Giới thiệu về Jest
Jestjs là một JavaScript Testing Framework khá là dễ sử dụng và cài đặt, tuy nhiên vẫn đầy đủ tính năng để bạn có thể sử dụng
Cài đặt
Cài đặt Jest khá đơn giản:
yarn add --dev jest
hoặc
npm install --save-dev jest
Sau đó thêm đoạn code sau vào file package.json:
{
"scripts": {
"test": "jest"
}
}
Tiếp theo mình có ví dụ
một file test: math.js
function sum[a, b] {
return a + b;
}
const MathJS = {
sum,
}
module.exports = MathJS;
Để viết test cho file trên mình tạo file test/math.test.js
, đuôi file là test.js sẽ nói cho jest biết đây là file test của bạn.
const MathJS = require['../math'];
it['Adds 1 + 1 to equals 2', [] => {
expect[MathJS.sum[1, 1]].toBe[2];
}];
Sau đó chạy yarn test
hoặc npm run test
.
Các Matchers trong Jest
Ở trong đoạn code trên:
expect[MathJS.sum[1, 1]].toBe[2];
.toBe[]
chính là một matcher trong
jest. Nó giống như phép so sánh bằng bình thường vậy. Ví dụ:
expect[result].toBe[2];
expect[result].toBe[true];
expect[result].toBe[{a: 1, b: 2}];
Tuy nhiên khi so sánh một Object bạn nên sử dụng .toEqual[]
Lý do là vì .toBe
thực tế sử dụng ===
để so sánh và đưa ra kết quả. Và chúng ta đều biết trong javascript:
a = {};
b = {};
a === b;
=> false
Còn .toEqual[]
theo như Jest sẽ lần lượt kiểm tra tất các trường của Object, hoặc mảng để so sánh. Vì vậy thay vì viết:
expect[result].toBe[{a: 1, b: 2}];
Hãy viết:
expect[result].toEqual[{a: 1, b: 2}];
Ngoài ra còn các matchers khác:
Truthiness
toBeNull
so sánh với giá trịnull
.toBeUndefined
so sánh với giá trịundefined
.toBeDefined
là hàm cho kết quả ngược lạitoBeUndefined
.toBeTruthy
so sánh với giá trị true.toBeFalsy
so sánh với giá trị false.
Numbers
it['two plus two', [] => {
const value = 2 + 2;
expect[value].toBeGreaterThan[3];
expect[value].toBeGreaterThanOrEqual[3.5];
expect[value].toBeLessThan[5];
expect[value].toBeLessThanOrEqual[4.5];
// toBe and toEqual are equivalent for numbers
expect[value].toBe[4];
expect[value].toEqual[4];
}];
Đối với số thập phân, bạn nên sử dụng toBeCloseTo
:
it['adding floating point numbers', [] => {
const value = 0.1 + 0.2;
//expect[value].toBe[0.3]; This won't work because of rounding error
expect[value].toBeCloseTo[0.3]; // This works.
}];
String
Bạn có thể kiểm tra một đoạn văn bản với regular expressions bằng toMatch
:
it['there is no I in team', [] => {
expect['team'].not.toMatch[/I/];
}];
it['but there is a "stop" in Christoph', [] => {
expect['Christoph'].toMatch[/stop/];
}];
Array
Để kiểm tra giá trị có trong một mảng, bạn có thể dùng toContain:
const array = [1, 2, 10, 1000];
it['array has 1000 on it', [] => {
expect[array].toContain[1000];
}];
Exceptions
Để kiểm tra một lỗi có thể xảy ra bạn có thể sử dụng toThrow
:
function compileAndroidCode[] {
throw new ConfigError['you are using the wrong JDK'];
}
test['compiling android goes as expected', [] => {
expect[compileAndroidCode].toThrow[];
expect[compileAndroidCode].toThrow[ConfigError];
// You can also use the exact error message or a regexp
expect[compileAndroidCode].toThrow['you are using the wrong JDK'];
expect[compileAndroidCode].toThrow[/JDK/];
}];
Một số ví dụ
Test một action trong redux
const CHANGE_EMAIL = 'CHANGE_EMAIL';
function changeEmail[email] {
return {
type: CHANGE_EMAIL,
email
}
}
it['should render type and email of change email action', [] => {
const email = '';
const expected = {
type: CHANGE_EMAIL,
email,
}
expect[changeEmail[email]].toEqual[expected];
}];
Test một event trong Jquery
it['should fire a alert', [] => {
const alert = jest.fn[];
document.body.innerHTML =
'' +
' ' +
' ' +
'';
$['#button'].click[[] => {
alert['click'];
}];
$['#button'].click[];
expect[alert].toBeCalled[];
// the mock function is called one time
expect[alert.mock.calls.length].toBe[1];
// The first argument of the first call to the function was click
expect[alert.mock.calls[0][0]].toBe['click'];
}];
Bạn có thể thấy const alert = jest.fn[];
. Đây là một tính năng trong Jest giúp bạn mock một function. Hay mô phỏng lại hàm cần test.
Test với module axios
const axios = require['axios'];
jest.mock['axios', [] => [{
get: jest.fn[].mockResolvedValue[{ data: { message: 'hello' }}]
}]];
test['mock axios.get', async [] => {
const response = await axios.get['//test.com/t/1'];
expect[response.data].toEqual[{ foo: 'bar' }];
}];
Trong ví dụ này mình đã mock module axios, và đặt giá trị trả về cho hàm get. Như vậy khi test bạn sẽ không cần phải gửi request thật, tránh mất thời gian. Các module khác bạn cũng có thể làm cách tương tự.
Blog của mình