Các hàm Generator trong TypeScript

Các hàm Generator trong TypeScript

Bài viết này giải thích về các hàm generator trong TypeScript.

Bạn có thể học mọi thứ từ cơ bản về cách sử dụng hàm generator đến các ví dụ nâng cao kết hợp với xử lý bất đồng bộ, kèm theo các mẫu mã.

YouTube Video

Các hàm Generator

Các hàm generator trong TypeScript cung cấp chức năng tương tự như các hàm generator trong JavaScript. Các hàm generator được định nghĩa bằng function* (một khai báo hàm có dấu hoa thị) và là các hàm đặc biệt có thể tạm dừng và tiếp tục thực thi, không giống như các hàm thông thường.

Khi một hàm generator được gọi, một iterator (bộ lặp) được trả về, tạo ra các giá trị từng cái một thông qua bộ lặp này, và bạn có thể tạm dừng thực thi sử dụng từ khóa yield hoặc gửi giá trị từ bên ngoài.

Cú pháp cơ bản của các hàm 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)
  • Định nghĩa một hàm generator bằng function* myGenerator().
  • Từ khóa yield tạm dừng thực thi hàm trong khi trả về một giá trị.
  • Mỗi lần phương thức next() được gọi, việc thực thi hàm generator tiếp tục và tiến tới lệnh yield tiếp theo.

next() trả về một đối tượng chứa giá trị kế tiếp và một thuộc tính done. Khi donetrue, điều này cho biết rằng tất cả các giá trị đã được tạo ra và quá trình xử lý của generator đã hoàn thành.

Các ứng dụng của các hàm Generator

Việc sử dụng các hàm generator cho phép dễ dàng biểu diễn xử lý tuần tự. Trong ví dụ sau, chúng ta tạo một hàm generator tạo ra một chuỗi các số.

 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
  • Trong ví dụ này, sequenceGenerator tạo ra một chuỗi số tăng không giới hạn. Sử dụng yield để trả về giá trị tại mỗi bước, và tạo ra giá trị tiếp theo trong các lần gọi sau.

Truyền giá trị vào next

Phương thức next() có thể nhận một giá trị, giá trị này có thể được gửi vào hàm 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
  • Trong ví dụ này, next(5)next(10) gửi các giá trị tương ứng của chúng vào hàm generator, và yield num1 + num2 trả về tổng của chúng.

returnthrow

  • return(value) có thể kết thúc generator và trả về giá trị được chỉ định.
  • throw(error) có thể tung ra một lỗi bên trong generator, được sử dụng để xử lý lỗi trong 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!
  • Trong ví dụ này, phương thức throw được sử dụng để tạo ra một lỗi bên trong generator, và lỗi đó được bắt trong generator.

Định nghĩa kiểu trong TypeScript

Định nghĩa kiểu của một hàm generator có thể được chỉ định bằng định dạng sau.

1// Generator<YieldType, ReturnType, NextType>
2function* myGenerator(): Generator<number, void, unknown> {
3    yield 1;
4    yield 2;
5    yield 3;
6}
  • Bạn chỉ định kiểu dưới dạng Generator<YieldType, ReturnType, NextType>.
    • YieldType là kiểu của giá trị được trả về bởi yield.
    • ReturnType là kiểu của giá trị được trả về bởi return.
    • NextType là kiểu của giá trị được truyền vào cho next().

Trong ví dụ sau, các kiểu cụ thể được chỉ định để sử dụng generator một cách an toàn với kiểu dữ liệu.

 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)

Generator và Xử lý Bất đồng bộ

Generator cũng có thể được sử dụng cho xử lý bất đồng bộ. Ví dụ, bạn có thể sử dụng yield để chờ kết quả của các thao tác bất đồng bộ trong khi tiếp tục xử lý tuần tự. Tuy nhiên, trong TypeScript hoặc JavaScript, async/await thường được sử dụng phổ biến hơn.

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}

Do đó, mặc dù bạn có thể xử lý các thao tác bất đồng bộ tuần tự với generator, chúng không được sử dụng phổ biến trong xử lý bất đồng bộ vì Promises và async/await tiện lợi hơn.

Tóm tắt

  • Hàm generator là các hàm đặc biệt được định nghĩa với function* mà có thể trả về giá trị với yield trong khi tạm dừng việc thực thi hàm.
  • Sử dụng next() để tiếp tục hàm generator và nhận giá trị. Ngoài ra, bạn có thể gửi giá trị vào generator bằng cách sử dụng next(value).
  • Bạn có thể sử dụng return()throw() để kết thúc hàm generator hoặc xử lý lỗi.
  • Khi sử dụng generator trong TypeScript, bạn có thể sử dụng các định nghĩa kiểu để viết mã an toàn về kiểu dữ liệu.

Generator là công cụ mạnh mẽ cho phép bạn kiểm soát việc lặp một cách linh hoạt.

Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.

YouTube Video