ฟังก์ชัน Generator ใน TypeScript
บทความนี้อธิบายเกี่ยวกับฟังก์ชัน Generator ใน TypeScript
คุณสามารถเรียนรู้ทุกอย่างตั้งแต่พื้นฐานของการใช้ฟังก์ชันตัวกำเนิดไปจนถึงตัวอย่างขั้นสูงที่ผสมผสานกับการประมวลผลแบบอะซิงโครนัส พร้อมตัวอย่างโค้ด
YouTube Video
ฟังก์ชัน Generator
ฟังก์ชัน Generator ใน TypeScript ทำงานในลักษณะที่คล้ายคลึงกับฟังก์ชัน Generator ใน JavaScript ฟังก์ชัน Generator ถูกกำหนดโดยใช้ function*
(การประกาศฟังก์ชันพร้อมเครื่องหมายดอกจัน) และเป็นฟังก์ชันพิเศษที่สามารถหยุดการทำงานชั่วคราวและกลับมาทำงานต่อได้ ไม่เหมือนฟังก์ชันทั่วไป
เมื่อเรียกใช้ฟังก์ชัน Generator จะคืนค่าเป็น iterator ซึ่งจะสร้างค่าทีละค่าโดยผ่าน iterator นี้ โดยสามารถหยุดการทำงานชั่วคราวได้โดยใช้คำสั่ง yield
หรือส่งค่าเข้ามาจากภายนอก
ไวยากรณ์พื้นฐานของฟังก์ชัน Generator
1function* myGenerator(): Generator<number, void, unknown> {
2 yield 1;
3 yield 2;
4 yield 3;
5}
6
7const gen = myGenerator();
8
9console.log(gen.next().value); // 1
10console.log(gen.next().value); // 2
11console.log(gen.next().value); // 3
12console.log(gen.next().done); // true (Iteration finished)
- กำหนดฟังก์ชัน Generator ด้วย
function* myGenerator()
- คำสั่ง
yield
จะหยุดการทำงานของฟังก์ชันชั่วคราวและส่งค่ากลับออกมา - ทุกครั้งที่เรียกใช้เมธอด
next()
การทำงานของฟังก์ชัน Generator จะกลับมาทำงานต่อและไปยังyield
ถัดไป
next()
จะคืนค่าเป็นออบเจ็กต์ที่ประกอบด้วยค่าถัดไปและพร็อพเพอร์ตี done
เมื่อ done
เป็น true
แสดงว่าค่าทั้งหมดถูกสร้างขึ้นแล้วและการทำงานของฟังก์ชัน Generator เสร็จสิ้น
การใช้งานของฟังก์ชัน Generator
การใช้ฟังก์ชัน Generator ช่วยให้สามารถแสดงขั้นตอนการประมวลผลแบบลำดับได้ง่าย ในตัวอย่างต่อไปนี้ เราจะสร้างฟังก์ชัน Generator ที่สร้างลำดับตัวเลข
1function* sequenceGenerator(start: number = 0, step: number = 1) {
2 let current = start;
3 while (true) {
4 yield current;
5 current += step;
6 }
7}
8
9const seq = sequenceGenerator(1, 2);
10
11console.log(seq.next().value); // 1
12console.log(seq.next().value); // 3
13console.log(seq.next().value); // 5
- ในตัวอย่างนี้
sequenceGenerator
สร้างลำดับตัวเลขที่เพิ่มขึ้นเรื่อย ๆ โดยไม่สิ้นสุด ใช้yield
เพื่อคืนค่าทีละขั้น และสร้างค่าถัดไปในการเรียกครั้งถัดไป
การส่งค่าผ่าน next
เมธอด next()
สามารถรับค่าและส่งค่านั้นไปยังฟังก์ชัน Generator ได้
1function* adder() {
2 const num1 = yield;
3 const num2 = yield;
4 yield num1 + num2;
5}
6
7const addGen = adder();
8addGen.next(); // Initialization
9addGen.next(5); // Set 5 to num1
10const result = addGen.next(10).value; // Set 10 to num2 and get result
11console.log(result); // 15
- ในตัวอย่างนี้
next(5)
และnext(10)
ส่งค่าของตัวเองเข้าไปในฟังก์ชัน Generator และyield num1 + num2
จะคืนค่าผลรวมของมัน
return
และ throw
return(value)
สามารถยุติการทำงานของฟังก์ชัน Generator และคืนค่าที่ระบุthrow(error)
สามารถโยนข้อผิดพลาดในฟังก์ชัน Generator และใช้เพื่อจัดการข้อผิดพลาดในฟังก์ชัน Generator
1function* testGenerator() {
2 try {
3 yield 1;
4 yield 2;
5 } catch (e) {
6 console.error("Error caught:", e);
7 }
8}
9
10const gen = testGenerator();
11console.log(gen.next().value); // 1
12gen.throw(new Error("An error occurred!")); // Error caught: An error occurred!
- ในตัวอย่างนี้ เมธอด
throw
ถูกใช้เพื่อสร้างข้อผิดพลาดในฟังก์ชัน Generator และข้อผิดพลาดนั้นจะถูกจัดการภายในฟังก์ชัน Generator
การกำหนดชนิดข้อมูลใน TypeScript
คำนิยามชนิดของฟังก์ชันตัวสร้างสามารถกำหนดได้ในรูปแบบต่อไปนี้
1// Generator<YieldType, ReturnType, NextType>
2function* myGenerator(): Generator<number, void, unknown> {
3 yield 1;
4 yield 2;
5 yield 3;
6}
- คุณระบุชนิดข้อมูลในรูปแบบของ
Generator<YieldType, ReturnType, NextType>
YieldType
คือประเภทของค่าที่ส่งกลับโดยyield
ReturnType
คือประเภทของค่าที่ส่งกลับโดยreturn
NextType
คือประเภทของค่าที่ส่งผ่านไปยังnext()
ในตัวอย่างต่อไปนี้ ได้ระบุชนิดข้อมูลที่เฉพาะเจาะจงเพื่อใช้ตัวกำเนิดอย่างปลอดภัยด้วยชนิดข้อมูล
1function* numberGenerator(): Generator<number, void, number> {
2 const num1 = yield 1;
3 const num2 = yield num1 + 2;
4 yield num2 + 3;
5}
6
7const gen = numberGenerator();
8
9console.log(gen.next().value); // 1
10console.log(gen.next(10).value); // 12 (10 + 2)
11console.log(gen.next(20).value); // 23 (20 + 3)
ตัวกำเนิดและการประมวลผลแบบอะซิงโครนัส
ตัวกำเนิดยังสามารถใช้สำหรับการประมวลผลแบบอะซิงโครนัสได้ด้วย ตัวอย่างเช่น คุณสามารถใช้ yield
เพื่อรอผลลัพธ์ของการดำเนินการแบบอะซิงโครนัสในขณะที่ดำเนินการตามลำดับต่อไป อย่างไรก็ตาม ใน TypeScript หรือ JavaScript async/await
จะถูกใช้บ่อยกว่า
1function* asyncTask() {
2 const result1 = yield fetch("https://codesparklab.com/json/example1.json");
3 console.log(result1);
4
5 const result2 = yield fetch("https://codesparklab.com/json/example2.json");
6 console.log(result2);
7}
ดังนั้น แม้ว่าคุณจะสามารถประมวลผลการดำเนินการแบบอะซิงโครนัสตามลำดับได้ด้วยตัวกำเนิด แต่มักจะไม่ได้ใช้งานกับการประมวลผลแบบอะซิงโครนัสเพราะ Promises และ async
/await
สะดวกมากกว่า
สรุป
- ฟังก์ชันตัวกำเนิด คือฟังก์ชันพิเศษที่ถูกกำหนดด้วย
function*
ซึ่งสามารถส่งคืนค่าด้วยyield
ในขณะที่หยุดการทำงานของฟังก์ชัน - ใช้
next()
เพื่อเริ่มกระบวนการตัวกำเนิดอีกครั้งและรับค่า นอกจากนี้ คุณยังสามารถส่งค่าเข้าไปในตัวกำเนิดได้โดยใช้next(value)
- คุณสามารถใช้
return()
และthrow()
เพื่อยุติฟังก์ชันตัวกำเนิดหรือจัดการข้อผิดพลาด - เมื่อใช้ตัวกำเนิดใน TypeScript คุณสามารถใช้การกำหนดชนิดข้อมูลเพื่อเขียนโค้ดที่มีความปลอดภัยตามชนิดข้อมูล
ตัวกำเนิดเป็นเครื่องมือที่ทรงพลังที่ช่วยให้คุณควบคุมการวนซ้ำได้อย่างยืดหยุ่น
คุณสามารถติดตามบทความข้างต้นโดยใช้ Visual Studio Code บนช่อง YouTube ของเรา กรุณาตรวจสอบช่อง YouTube ด้วย