Ở bài này ta sẽ dựng một Server NodeJS, và chuyển việc mô phỏng được thực thi trên client ở trang web để tìm hiểu thêm về cách hoạt động Promise liên tục này.
server.js
.home.ejs
đơn giản (trong thư mục views
) mô phỏng Promise để nghiên cứu cách hoạt động, ở đây ta sẽ sử dụng luôn function add phép cộng đối tượng Promise mô phỏng bất đồng bộ ở bài trước để sử dụng.// File server.js
let express = require('express');
let app = express();
app.listen(3000);
app.set('view engine', 'ejs');
app.set('views', './views');
app.get('/', (req, res) => {
res.render('home');
});
<!-- home.ejs -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Lập trình Nodejs: Xử lý bất đồng bộ trong Javascript</title>
<style>
body {
height: 100vh;
background: linear-gradient(to top left, #28b487, #7dd56f);
}
h1 {
text-align: center;
color: white;
}
</style>
<script type="text/javascript">
let add = (a, b) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (typeof a != 'number' || typeof b != 'number') {
return reject(
new Error('Tham số truyền vào phải là kiểu number!')
);
}
resolve(a + b);
}, 2000);
});
};
</script>
<!-- <script src="./script.js"></script> -->
</head>
<body>
<h1>Lập trình Nodejs: Xử lý bất đồng bộ trong Javascript</h1>
<h2>Promise status và Promise value</h2>
</body>
</html>
Không tách được file script.js ra được
), phải để chung script trong file home.ejs
, ta sẽ thao tác trên<script type="text/javascript">
...
</script>
để tìm hiểu về cách hoạt động promise trên client.
let add = (a, b) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (typeof a != 'number' || typeof b != 'number') {
return reject(new Error('Tham số truyền vào phải là kiểu number!'));
}
resolve(a + b);
}, 1000);
});
};
add(4, 5).then(
(res) => console.log(res),
(err) => console.log(err + '')
);
server.js
lên.localhost:3000
let a = add(4, 5);
console.log(a);
Để ý ở đây thấy:
Promise {<pending>}
: Đang ở trạng thái Pending[[Prototype]]: Promise
: Nguyên mẫu là Promise[[PromiseState]]: "fulfilled"
:[[PromiseResult]]: 9
: Kết quả trả về là 9, của Add(4,5)
Khi xem kiểm tra các trình duyệt thì mỗi trình duyệt có cách thể hiện khác nhau.
<state>: "pending"
), chưa có giá trị => (<value>: undefined
)<state>: "fulfilled"
). có giá trị là 9 (<value>: 9
)<state>: "fulfilled"
) và giá trị là 9 (<value>: 9
).// Log lần 1
let a = add(4, '5');
console.log(a);
// Log lần 2 sau 2500ms
setTimeout(() => {
console.log(a);
}, 2500);
<state>: "rejected"
), giá trị trả về là Error(<reason>: Error: Tham số truyền vào phải là kiểu number!
)// Log lần 1
let a = add(4, 5).then(
(result) => console.log(result),
(err) => console.log(err + '')
);
console.log(a);
// Log lần 2 sau 2500ms
setTimeout(() => {
console.log(a);
}, 2500);
console.log(a);
là 9.fulfilled
, nhưng value vẫn là undefined (<value>: undefined
)// Log lần 1
let a = add(4, 5).then(
(result) => 'Kết quả: ' + result,
(err) => console.log(err + '')
);
console.log(a);
// Log lần 2 sau 2500ms
setTimeout(() => {
console.log(a);
}, 2500);
Lúc này lần log 2 đã thể hiện giá trị Value là <value>: "Kết quả: 9"
; như vậy lần log 2 sẽ căn cứ vào giá trị trả về của .then resolve trả về mà không phải dựa trên kết quả của add(4,5) là 9.
Ta thay đổi lại tham số truyền vào kiểu text để xuất hiện lỗi và thay đổi reject để tìm hiểu giá trị log lần 2.
// Log lần 1
let a = add(4, '5').then(
(result) => 'Kết quả: ' + result,
(err) => 'Lỗi: ' + err + ''
);
console.log(a);
// Log lần 2 sau 2500ms
setTimeout(() => {
console.log(a);
}, 2500);
<value>: "Lỗi: Error: Tham số truyền vào phải là kiểu number!"
, mặc dù ở xử lý add(4,’5’) đã xuất hiện lỗi, nhưng vẫn lấy giá trị trả về lỗi này gán cho giá trị a.Như vậy giá trị a được trả về sẽ căn cứ việc có thực hiện hay bị lỗi, và giá trị trả về sẽ được gán cho a; ngoài ra ta sẽ thấy trạng thái mặc dù xuất hiện lỗi ở Log 1 nhưng ở lần Log 2 vẫn thể hiện <state>: "fulfilled"
hoàn thành xử lý gán cho a.
add(4,'5')
) rơi vào (err) => 'Lỗi: ' + err + ''
, nhưng trạng thái trả về vẫn là fulfilled bởi vì khi xảy ra lỗi thì ta có function xử lý lỗi, và sau khi xử lý lỗi thì sẽ báo việc xử lý lỗi đã được hoàn thành là fulfilled// Log lần 1
let a = add(4, '5');
console.log(a);
// Log lần 2 sau 2500ms
setTimeout(() => {
console.log(a);
}, 2500);
<state>: "rejected"
bị từ chối chưa được xử lý lỗi.// Log lần 1
let a = add(4, '5')
.then(
(result) => 'Kết quả: ' + result,
(err) => 'Lỗi: ' + err + ''
)
.then(() => console.log('Thành công rồi!'));
console.log(a);
// Log lần 2 sau 2500ms
setTimeout(() => {
console.log(a);
}, 2500);
// Log lần 1
// Log lần 1
let a = add(4, 5)
.then(
(result) => {
console.log('.then thứ nhất: ' + result);
return 'Kết quả: ' + result;
},
(err) => 'Lỗi: ' + err + ''
)
.then((b) => console.log('Thành công rồi!' + b));
console.log(a);
// Log lần 2 sau 2500ms
setTimeout(() => {
console.log(a);
}, 2500);
Như vậy tham số b của .then thứ 2 sẽ là kết quả return ở resolve của .then thứ 1, việc value undefined là do .then thứ 2 trả về console.log làm cho giá trị a không xác định.
Ta điều chỉnh xuất hiện lỗi để nhảy vào reject của .then thứ 1 để kiểm tra kết quả như sau:
// Log lần 1
let a = add(4, '5')
.then(
(result) => {
console.log('.then thứ nhất: ' + result);
return 'Kết quả: ' + result;
},
(err) => {
console.log(err + '');
return 'Bị lỗi.';
}
)
.then((b) => console.log('Thành công rồi!' + b));
console.log(a);
// Log lần 2 sau 2500ms
setTimeout(() => {
console.log(a);
}, 2500);
Ta đã thấy được thứ tự câu lệnh .then liên tiếp trong một Promise và sự thừa hưởng giá trị return trả về có thể được sử dụng tiếp bằng tham số truyền vào câu lệnh .then kế tiếp, dù cho .then đầu tiên có resolve hay reject thì return handle sẽ được sử dụng làm tham số của câu lệnh .then thứ 2.
Ta điều chỉnh lại ở .then 2 ta trả return về giá trị để gán cho a thì kết quả hiển thị log lần 2 sẽ là:
// Log lần 1
let a = add(4, '5')
.then(
(result) => {
console.log('.then thứ nhất: ' + result);
return 'Kết quả: ' + result;
},
(err) => {
console.log(err + '');
return 'Bị lỗi.';
}
)
.then((b) => 'Then thứ 2 - ' + b);
console.log(a);
// Log lần 2 sau 2500ms
setTimeout(() => {
console.log(a);
}, 2500);