Hi 🤓 Cảm ơn bạn đã ghé thăm blog này, nếu những bài viết trên blog giúp ích cho bạn. Bạn có thể giúp blog hiển thị quảng cáo bằng cách tạm ngừng ad blocker 😫 và để giúp blog duy trì hoạt động nếu bạn muốn.
Cảm ơn bạn!

Chào các bạn, hôm nay chúng ta lại tiếp tục với các bài viết về học JavaScript cơ bản nhé 😄. Ở bài viết trước chúng ta đã tìm hiểu về Iterators và Iterables trong JavaScript. Trong bài viết này, chúng ta sẽ học về generators trong JavaScript và làm thế nào để sử dụng chúng nhé!

JavaScript Generators

Như chúng ta đã biết, một function thông thường trong JavaScript không thể tạm dừng giữa chừng và sau đó tiếp tục tại nơi nó đã tạm dừng.

Ví dụ:

function display() {
    console.log('Đây là');
    console.log('blog');
    console.log('homiedev.com');
}

Ở ví dụ này, hàm display() thực thi từ trên xuống. Cách để thoát display() là sử dụng return hoặc đưa ra một lỗi. Nếu chúng ta gọi lại hàm display(), nó sẽ bắt đầu thực thi từ trên xuống.

display();

Kết quả:

Đây là
blog
homiedev.com

Vậy làm thế nào để tạm dừng một hàm và sau đó tiếp tục tại nơi nó đã tạm dừng?

ES6 giới thiệu một loại hàm mới khác với một hàm thông thường gọi là function generator hoặc generator. Sử dụng hàm này, chúng ta có thể tạm dừng và tiếp tục tại nơi đã tạm dừng. Một generator được sử dụng như ví dụ sau:

function* goGroceryShopping() {
    console.log('Dừng lại lần 1');
    yield 'hamburger - 🍔';
    console.log('Dừng lại lần 2');
    yield 'meat - 🥩';
}

Ở hàm goGroceryShopping():

  1. Đầu tiên, chúng ta sử dụng dấu hoa thị (*) sau function keyword. Dấu hoa thị biểu thị rằng goGroceryShopping() là một generator, không phải là một hàm thông thường.
  2. Sử dụng câu lệnh yield trả về một giá trị và tạm dừng việc thực thi hàm.

Bây giờ ta thử gọi hàm goGroceryShopping() nhé:

let gen = goGroceryShopping();

Ở đoạn code trên, đầu tiên chúng ta sẽ thấy không có gì ở console. Nếu goGroceryShopping() là một hàm thông thường thì chúng ta sẽ thấy dữ liệu hiển thị ở console.

Chúng ta đã thực thi hàm goGroceryShopping(), lúc này giá trị trả về từ hàm này sẽ được gán cho biến gen, cùng xem kết quả trả về là gì nhé.

console.log(gen);

Kết quả:

Generator { }

Trong ví dụ này, generator trả về một object Generator mà không thực thi phần thân của nó khi nó được gọi.

Object Generator trả về một object khác có hai thuộc tính (properties) là: donevalue. Nói cách khác, object Generator là iterable.

Để tìm hiểu về iterable và những thứ liên quan, bạn đọc bài viết: Iterators và Iterables trong JavaScript bạn đã biết chưa?.

Bây giờ chúng ta thử sử dụng method next() của object Generator.

let result = gen.next();

console.log(result);

Kết quả:

Dừng lại lần 1
{value: 'hamburger - 🍔', done: false}

Chúng ta có thể thấy, object Generator thực thi phần thân của nó, sau đó hiển thị thông báo 'Dừng lại lần 1' ở dòng 1 và trả về giá trị 'hamburger - 🍔' ở dòng 2.

Câu lệnh yield trả về 'hamburger - 🍔' và tạm dừng generator ở dòng 2.

Tương tự, chúng ta sẽ gọi method next() của Generator lần thứ hai:

result = gen.next();

console.log(result);

Kết quả:

Dừng lại lần 2
{value: 'meat - 🥩', done: false}

Lần này Generator tiếp tục thực thi từ dòng 3 và hiển thị thông báo 'Dừng lại lần 2' và trả về 'meat - 🥩'.

Ở lần gọi thứ 3, ta sẽ nhận được kết quả:

{value: undefined, done: true}

generator là iterable, chúng ta có thể sử dụng vòng lặp for ... of:

for (const element of gen) {
    console.log(element);
}

Kết quả chúng ta nhận được:

Dừng lại lần 1
hamburger - 🍔
Dừng lại lần 2
meat - 🥩

Một ví dụ khác sử dụng generator:

function* infinite() {
  let index = 0;

  while (true) {
    yield index++;
  }
}

const generator = infinite();

console.log(generator.next().value); // 0
console.log(generator.next().value); // 1
console.log(generator.next().value); // 2

Ở ví dụ này, mỗi khi chúng ta gọi method next(), nó sẽ trả về số tiếp theo. Chi tiết về vòng lặp while bạn đọc tại: Vòng lặp while trong JavaScript.

Triển khai iterator

Khi muốn triển khai một iterator, chúng ta phải tạo thủ công method next(). Vì generator là iterable, nên chúng có thể giúp chúng ta đơn giản hóa việc triển khai iterator.

Mình xin sử dụng ví dụ từ bài viết Iterators và Iterables trong JavaScript:

const obj = {
  blog: "homiedev.com",
  categories: ["ReactJS", "JavaScript", "TypeScript"],
  isFrontEndBlog: true,
};

obj[Symbol.iterator] = function () {
  let i = 0;
  let values = Object.values(this);

  return {
    next: () => {
      return {
        value: values[i++],
        done: i > values.length,
      };
    },
  };
};

Bây giờ, chúng ta có thể sử dụng generator để triển khai iterator như sau:

const obj = {
  blog: "homiedev.com",
  categories: ["ReactJS", "JavaScript", "TypeScript"],
  isFrontEndBlog: true,

  *[Symbol.iterator]() {
    const values = Object.values(this);

    for (let index = 0; index < values.length; index++) {
      const element = values[index];

      yield element;
    }
  },
};

for (const element of obj) {
  console.log(element);
}

Như các bạn có thể thấy, method Symbol.iterator nhìn đơn giản hơn khi chúng ta sử dụng generator.

Kết quả sẽ như sau:

homiedev.com
['ReactJS', 'JavaScript', 'TypeScript']
true

Bằng cách sử dụng generator chúng ta có thể tạo một chức năng quản lý dữ liệu như thêm phần tử vào danh sách, lặp qua những phần tử đã thêm vào danh sách, ví dụ:

class List {
  constructor() {
    this.elements = [];
  }
  count() {
    return this.elements.length;
  }
  isEmpty() {
    return this.elements.length === 0;
  }
  add(element) {
    this.elements.push(element);
  }
  *[Symbol.iterator]() {
    for (let element of this.elements) {
      yield element;
    }
  }
}

let list = new List();

list.add("Homiedev");
list.add("Front-End");
list.add("JavaScript");

for (let item of list) {
  console.log(item);
}

Kết quả:

Homiedev
Front-End
JavaScript

Như vậy là chúng ta đã tìm hiểu qua generator trong JavaScript. Hi vọng bài viết giúp ích cho các bạn. Chúng ta sẽ gặp lại nhau trong các bài viết sắp tới nhé 😁.

Có thể bạn thích ⚡
homiedev
About Me

Hi, I'm @devnav. Một người thích chia sẻ kiến thức, đặc biệt là về Frontend 🚀. Trang web này được tạo ra nhằm giúp các bạn học Frontend hiệu quả hơn 🎉😄.

Chúc các bạn tìm được kiến thức hữu ích trong blog này 😁😁.