ฟังก์ชัน Generator ใน TypeScript

ฟังก์ชัน 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 ด้วย

YouTube Video