การทำงานของ node js จะทำงานแบบ asynchronous event-driven JavaScript runtime คือ จะทำงานแบบไม่เรียงลำดับ
function simulateTimeOut(text, timeout) {
setTimeout(() => {
console.log(text);
}, timeout);
}
// call functions
simulateTimeOut('A', 1000)
simulateTimeOut('B', 500)
simulateTimeOut('C', 700)
// --- results ---
B
C
A
จาก results จะเห็นได้ว่า function ไหนที่ทำงานเสร็จก่อนก็จะแสดงข้อมูลออกมาก่อน
แต่ถ้าเราอยากให้ program มันทำงานแบบเรียงตามลำดับ A B C สามารถทำได้ 3 วิธี ดังนี้
- callback
- Promise
- async-await
Callback
function simulateTimeOut(text, timeout, callback) {
setTimeout(() => {
console.log(text);
callback();
}, timeout);
}
// call functions
simulateTimeOut('A', 1000, () => {
simulateTimeOut('B', 500, () => {
simulateTimeOut('C', 700, () => {
console.log('Finish callback action');
})
})
})
// --- results ---
A
B
C
Finish callback action
หลักการของ callback คือ ทำงานใน function simulateTimeOut(text, timeout, callback) เสร็จก่อนแล้วค่อยทำงานใน function callback
แต่ถ้าเรามาการเรียก callback ซ้ำๆกันหลายอันมันก็จะเกิด callback hell ทำให้ยากต่อการอ่าน code จึงมี Promise มาช่วยแก้ปัญหานี้
Promise
function simulateTimeOut(text, timeout) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (text === 'D') return reject('D get rejected');
console.log(text);
resolve();
}, timeout);
})
}
// call functions
simulateTimeOut('A', 1000)
.then(() => {
return simulateTimeOut('B', 500)
})
.then(() => {
return simulateTimeOut('D', 700)
})
.then(() => {
return simulateTimeOut('C', 200)
})
.catch((error) => {
console.error(error)
})
.finally(() => {
console.log('Finished');
})
// --- results ---
A
B
D get rejected
Finished
.then()
เป็นการทำงานต่อ เมื่อการทำงานใน simulateTimeOut
เสร็จและ success นอกจากนั้นเรายังสามารถส่ง data เพื่อมาทำงานต่อใน then โดย ส่งผ่าน resolve(someData);
ใน simulateTimeOut
.catch()
เป็นการทำงานต่อ เมื่อการทำงานใน simulateTimeOut
เสร็จแต่ error เราสามารถส่ง error data เพื่อมาทำงานต่อใน catch โดย ส่งผ่าน reject(someError);
ใน simulateTimeOut
จะเห็นได้ว่า function C ไม่ทำงาน เนื่องจาก function D ได้รับ error or rejected
.finally()
จะทำงานเมื่อการทำงานทั้งหมดเสร็จสิ้น
async-await
จะเห็นได้ว่า promise กับ async-await มีความคล้ายกันแต่จะต่างกันที่การเรียกใช้งาน โดยใส่ async
ไปที่หน้า function และใส่ await
ไปที่หน้า function ที่เรียกใช้งานเพื่อบอกให้ function นั้นรอจนกว่าจะทำงานให้เสร็จแล้วค่อยไปทำงานในบรรทัดถัดไป
ซึ่งการ handle error ก็ทำได้โดยใส่ try-catch ได้เลย
function simulateTimeOut(text, timeout) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (text === 'D') return reject('D get rejected');
console.log(text);
resolve();
}, timeout);
})
}
// async function
async function run() {
try {
await simulateTimeOut('A', 1000)
await simulateTimeOut('B', 500)
await simulateTimeOut('C', 100)
} catch (error) {
console.error(error)
}
}
// call function
run()
// --- results ---
A
B
C