การทำงานกับ Promises ใน JavaScript
ในโลกของการเขียนโปรแกรม JavaScript, การจัดการกับการทำงานที่ไม่พร้อมกัน (Asynchronous Programming) เป็นเรื่องที่หลีกเลี่ยงไม่ได้ โดยเฉพาะเมื่อเราต้องดึงข้อมูลจากเซิร์ฟเวอร์ หรือทำงานที่ใช้เวลานาน การทำงานเหล่านี้อาจทำให้โปรแกรมของเราหยุดชะงัก (Blocking) หากเราจัดการไม่ดี Promises เข้ามาช่วยแก้ปัญหานี้ ทำให้เราเขียนโค้ดที่อ่านง่ายและจัดการกับการทำงานที่ไม่พร้อมกันได้อย่างมีประสิทธิภาพ
Asynchronous Programming คืออะไร?
ก่อนที่จะไปถึง Promises เรามาทำความเข้าใจ Asynchronous Programming กันก่อน เมื่อเราเขียนโค้ดแบบ Synchronous โปรแกรมจะทำงานทีละบรรทัดตามลำดับ แต่ใน Asynchronous Programming โปรแกรมสามารถเริ่มทำงานบางอย่าง และดำเนินการต่อโดยไม่ต้องรอให้งานนั้นเสร็จสิ้น เมื่องานนั้นเสร็จสิ้นแล้ว โปรแกรมจะกลับมาจัดการกับผลลัพธ์ของงานนั้น ตัวอย่างเช่น การดึงข้อมูลจาก API:
console.log("เริ่มต้นการทำงาน");
setTimeout(() => {
console.log("ดึงข้อมูลจาก API เสร็จสิ้น");
}, 2000); // รอ 2 วินาที
console.log("ทำงานอื่นๆ ต่อไป");
ในตัวอย่างนี้ `setTimeout` เป็นฟังก์ชัน Asynchronous ที่จะทำงานหลังจาก 2 วินาที โปรแกรมจะไม่รอให้ `setTimeout` ทำงานเสร็จ แต่จะพิมพ์ "ทำงานอื่นๆ ต่อไป" ทันที
Promises คืออะไร?
Promise คือ Object ที่เป็นตัวแทนของผลลัพธ์ของการทำงานที่ไม่พร้อมกัน ซึ่งอาจจะสำเร็จ (Resolved) หรือล้มเหลว (Rejected) Promise ช่วยให้เราจัดการกับการทำงานที่ไม่พร้อมกันได้อย่างเป็นระเบียบ โดยใช้ `.then()` เพื่อจัดการกับผลลัพธ์เมื่อสำเร็จ และ `.catch()` เพื่อจัดการกับข้อผิดพลาดเมื่อล้มเหลว
การสร้าง Promise
เราสามารถสร้าง Promise ได้โดยใช้ Constructor `new Promise()` Constructor นี้รับ Function ที่มี Parameter สองตัวคือ `resolve` และ `reject`:
- `resolve`: Function ที่ถูกเรียกเมื่อการทำงานสำเร็จ
- `reject`: Function ที่ถูกเรียกเมื่อการทำงานล้มเหลว
ตัวอย่างการสร้าง Promise:
const myPromise = new Promise((resolve, reject) => {
// ทำงานบางอย่างที่อาจสำเร็จหรือล้มเหลว
const success = true; // หรือ false
if (success) {
resolve("การทำงานสำเร็จ!");
} else {
reject("การทำงานล้มเหลว!");
}
});
ในตัวอย่างนี้ ถ้า `success` เป็น `true` เราจะเรียก `resolve` พร้อมกับส่งค่า "การทำงานสำเร็จ!" ไปด้วย ถ้า `success` เป็น `false` เราจะเรียก `reject` พร้อมกับส่งค่า "การทำงานล้มเหลว!" ไปด้วย
สถานะของ Promise
Promise มีสถานะ 3 สถานะ:
- Pending: สถานะเริ่มต้น, ยังไม่สำเร็จหรือล้มเหลว
- Resolved (Fulfilled): การทำงานสำเร็จ
- Rejected: การทำงานล้มเหลว
เมื่อ Promise เปลี่ยนสถานะจาก Pending ไปเป็น Resolved หรือ Rejected สถานะจะไม่สามารถเปลี่ยนแปลงได้อีก
การใช้ .then() และ .catch()
เราใช้ `.then()` เพื่อจัดการกับผลลัพธ์เมื่อ Promise เปลี่ยนสถานะเป็น Resolved และใช้ `.catch()` เพื่อจัดการกับข้อผิดพลาดเมื่อ Promise เปลี่ยนสถานะเป็น Rejected
ตัวอย่างการใช้งาน `.then()` และ `.catch()`:
myPromise
.then((result) => {
// จัดการกับผลลัพธ์เมื่อสำเร็จ
console.log("ผลลัพธ์:", result); // ผลลัพธ์: การทำงานสำเร็จ!
})
.catch((error) => {
// จัดการกับข้อผิดพลาดเมื่อล้มเหลว
console.error("ข้อผิดพลาด:", error); // ข้อผิดพลาด: การทำงานล้มเหลว! (ถ้า success เป็น false)
});
`.then()` รับ Function เป็น Parameter ซึ่ง Function นี้จะถูกเรียกเมื่อ Promise เปลี่ยนสถานะเป็น Resolved และได้รับค่าที่ส่งมาจาก `resolve` เป็น Argument
`.catch()` รับ Function เป็น Parameter ซึ่ง Function นี้จะถูกเรียกเมื่อ Promise เปลี่ยนสถานะเป็น Rejected และได้รับค่าที่ส่งมาจาก `reject` เป็น Argument
Chaining Promises
เราสามารถ Chain Promises ได้ โดยการ Return Promise ใหม่จาก `.then()` Function:
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { id: 1, name: "Example Data" };
resolve(data);
}, 1000);
});
};
const processData = (data) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const processedData = { ...data, processed: true };
resolve(processedData);
}, 500);
});
};
fetchData()
.then((data) => {
console.log("Data fetched:", data);
return processData(data); // Return Promise ใหม่
})
.then((processedData) => {
console.log("Processed data:", processedData);
})
.catch((error) => {
console.error("Error:", error);
});
ในตัวอย่างนี้ `fetchData` และ `processData` ต่างก็ Return Promise เมื่อ `fetchData` สำเร็จ เราจะเรียก `processData` และ Return Promise ที่ได้จาก `processData` ไปยัง `.then()` ตัวถัดไป ทำให้เราสามารถทำงานหลายขั้นตอนต่อเนื่องกันได้อย่างเป็นระเบียบ
การจัดการ Error ใน Promise Chain
ถ้ามี Error เกิดขึ้นใน Promise Chain, `.catch()` ตัวแรกที่เจอจะถูกเรียก:
const fetchDataWithError = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject("เกิดข้อผิดพลาดในการดึงข้อมูล");
}, 1000);
});
};
fetchDataWithError()
.then((data) => {
console.log("Data fetched:", data);
})
.then((processedData) => {
console.log("Processed data:", processedData);
})
.catch((error) => {
console.error("Error:", error); // Error: เกิดข้อผิดพลาดในการดึงข้อมูล
});
ในตัวอย่างนี้ `fetchDataWithError` จะ Reject Promise ด้วย Error message ดังนั้น `.catch()` จะถูกเรียกทันที และ `.then()` ทั้งสองตัวจะไม่ถูกเรียก
Promise.all()
`Promise.all()` เป็น Static Method ที่รับ Array ของ Promises และ Return Promise ใหม่ที่ Resolved เมื่อ Promises ทั้งหมดใน Array Resolved หรือ Rejected เมื่อมี Promise ใด Promise หนึ่งใน Array Rejected:
const promise1 = Promise.resolve(1);
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 200));
const promise3 = new Promise((resolve) => setTimeout(() => resolve(3), 100));
Promise.all([promise1, promise2, promise3])
.then((values) => {
console.log(values); // [1, 2, 3]
})
.catch((error) => {
console.error("Error:", error);
});
ในตัวอย่างนี้ `Promise.all()` จะรอให้ `promise1`, `promise2`, และ `promise3` Resolved แล้ว Return Array ของผลลัพธ์
Promise.race()
`Promise.race()` เป็น Static Method ที่รับ Array ของ Promises และ Return Promise ใหม่ที่ Resolved หรือ Rejected ตาม Promise ตัวแรกที่ Resolved หรือ Rejected:
const promise4 = new Promise((resolve) => setTimeout(() => resolve(4), 500));
const promise5 = new Promise((resolve) => setTimeout(() => resolve(5), 100));
Promise.race([promise4, promise5])
.then((value) => {
console.log(value); // 5
})
.catch((error) => {
console.error("Error:", error);
});
ในตัวอย่างนี้ `promise5` Resolved ก่อน `promise4` ดังนั้น `Promise.race()` จะ Resolved ด้วยค่า 5
สรุป
Promises เป็นเครื่องมือที่สำคัญในการจัดการกับการทำงานที่ไม่พร้อมกันใน JavaScript ช่วยให้โค้ดของเราอ่านง่ายและจัดการกับ Error ได้อย่างมีประสิทธิภาพ เราได้เรียนรู้เกี่ยวกับการสร้าง Promise, สถานะของ Promise, การใช้ `.then()` และ `.catch()`, การ Chain Promises, และการใช้ `Promise.all()` และ `Promise.race()` ในบทความถัดไป เราจะมาเรียนรู้เกี่ยวกับ `async/await` ซึ่งเป็น Syntax ที่ช่วยให้การเขียนโค้ด Asynchronous ง่ายยิ่งขึ้นไปอีก