Javascript

Generator function trong JS

Generator function in js

Hà Nội, những ngày tự cách ly cộng đồng không được đến công ty làm. Cả ngày chỉ quanh quẩn ở trong dãy nhà trọ trật hẹp, nhưng cũng là khoảng thời gian tốt để xây dựng kế hoạch cho tương lai.

1. Mở đầu

Như đã giới thiệu lúc đầu, Generator function không giống như function thông thường – chạy từ đầu tới cuối, gặp chỗ nào return thì tự break khỏi function, nó có những tính chất rất riêng – giống một vòng lặp hơn, cần thì dừng với yield và nếu muốn thì sẽ tiếp tục với next(). Chi tiết thế nào bạn hãy cùng xem ở phần dưới nhé. :v

2. Chi tiết

Đầu tiên, chúng ta hãy xem lại một function bình thường trong JS:

function normalFunction() {
    console.log('It')
    console.log('cannot')
    console.log('be')
    console.log('stopped.')
};

normalFunction();

Như các bạn đã thấy đây là một function bình thường, khi nó được chạy các câu lệnh bên trong nó sẽ được chạy từ đầu tới cuối. Nếu dừng thì cũng là lúc nó thoát ra như khi ta return, throw error() hoặc nếu như nó call lại nó thì cũng sẽ lại chạy từ đầu và nó trở thành đệ quy ^^.

Nhưng với Generator function thì lại rất khác đó chính là nó có thể dừng và tiếp tục chạy từ chính điểm dừng đó.

function * generatorFunction() {
    console.log('This will be executed first.');
    yield 'Hello, '; 

    console.log('I will be printed after the pause');  
    yield 'World!';
}

const generatorObject = generatorFunction();
console.log(generatorObject.next().value);
console.log(generatorObject.next().value);
console.log(generatorObject.next().value);

// This will be executed first.
// Hello, 
// I will be printed after the pause
// World!
// undefined

Như các bạn đã thấy chúng ta có thể thấy khi tạo một generator function thì chúng ta phải thêm dấu * vào giữa function keyword và name function đây là một loại syntax bắt buộc để các bạn tạo function loại này nhé. Bạn có thể sử dụng loại function như một function thường, nó có thể là các method trong class cũng có thể là method trong object.

Nhìn vào đoạn code trên có thể thấy ta không dùng return nữa mà thay vào đó lại dùng yield keyword. Đây là một loại keyword để đánh dấu các step bên trong một Generator function. Với đoạn code ở trên mỗi lần chúng ta next là một lần một step yield được handle. Và nó sẽ dừng ở đó thôi, nó sẽ không handle tiếp các đoạn statement ở dưới nữa.

function * generatorFunction() {
    console.log('This will be executed first.');
    yield 'Hello, '; 

    console.log('I will be printed after the pause');  
    yield 'World!';
}

const generatorObject = generatorFunction();
console.log(generatorObject.next().value);

// This will be executed first.
// Hello, 

Và nếu như vậy thì khi ta tiếp tục next thì nó sẽ handle khối yield nữa có đúng không? Chính xác ^^

function * generatorFunction() {
    console.log('This will be executed first.');
    yield 'Hello, '; 

    console.log('I will be printed after the pause');  
    yield 'World!';
}

const generatorObject = generatorFunction();
console.log(generatorObject.next().value);
console.log(generatorObject.next().value);


// This will be executed first.
// Hello, 
// I will be printed after the pause
// World!

Thế lại đặt câu hỏi thêm là nếu mình next tiếp thì sao? Nó sẽ log ra value là undefined các bạn nhé.

function * generatorFunction() {
    console.log('This will be executed first.');
    yield 'Hello, '; 

    console.log('I will be printed after the pause');  
    yield 'World!';
}

const generatorObject = generatorFunction();
console.log(generatorObject.next().value);
console.log(generatorObject.next().value);
console.log(generatorObject.next().value);
console.log(generatorObject.next().value);
console.log(generatorObject.next().value);

// This will be executed first.
// Hello, 
// I will be printed after the pause
// World!
// undefined
// undefined
// undefined

Cho dù ta có next quá 2 lần tương ứng với 2 yield bao nhiêu lần đi nữa thì nó vẫn ra undefined thôi nhé.

Lại có bạn hỏi thêm: Tại sao lại phải gắn Generator function vào biến, sao không call thẳng nó đi, thì xin trả lời là nếu call thẳng nó sẽ không thể lưu lại vị trí mà nó dừng trước để từ đó mà call tiếp, nếu call thằng thì nó chỉ chạy được yield ban đầu thôi.

function * generatorFunction() {
    console.log('This will be executed first.');
    yield 'Hello, '; 

    console.log('I will be printed after the pause');  
    yield 'World!';
}

console.log(generatorFunction().next().value);
console.log(generatorFunction().next().value);
console.log(generatorFunction().next().value);

// This will be executed first.
// Hello, 
// This will be executed first.
// Hello, 
// This will be executed first.
// Hello, 

Lại có bạn hỏi thế bên trong next() method kia nó return ra cái gì sao mà lấy được value như vậy? Các bạn hãy xem đoạc code bên dưới nhé.

function * generatorFunction() {
    console.log('This will be executed first.');
    yield 'Hello, '; 
}

const generatorObject = generatorFunction();
console.log(generatorObject.next());
// This will be executed first.
// {value: "Hello, ", done: false}

Như vậy nó sẽ return ra 1 object có 2 keys là value và done. Và khi mà done: true thì nghĩa là function đã finish và nếu ta gọi tiếp thì chỉ có thể nhận được giá trị là undefined mà thôi.

function * generatorFunction() {
    console.log('This will be executed first.');
    yield 'Hello, '; 
}

const generatorObject = generatorFunction();
console.log(generatorObject.next());
console.log(generatorObject.next());
console.log(generatorObject.next());

// This will be executed first.
// {value: "Hello, ", done: false}
// {value: undefined, done: true}
// {value: undefined, done: true}

Lại có bạn hỏi tiếp, nếu mình dùng return ở trong Generator function thì có được không? Câu trả lời là được . Of course !

function * generatorFunction() {
    console.log('This will be executed first.');
    yield 'Hello, '; 

    return;
    console.log('I will be printed after the pause');  
    yield 'World!';
}

const generatorObject = generatorFunction();
console.log(generatorObject.next());
console.log(generatorObject.next());
console.log(generatorObject.next());
console.log(generatorObject.next());
console.log(generatorObject.next());

// This will be executed first.
// {value: "Hello, ", done: false}
// {value: undefined, done: true}
// {value: undefined, done: true}
// {value: undefined, done: true}
// {value: undefined, done: true}

Nhưng nó sẽ finish ngay sau lần đầu tiên ta gọi và những giá trị tiếp theo sẽ là undefined.

3. Kết luận

Như vậy, Generator function là một tính năng rất hay trong JS. Nó có thể xứ lí bất đồng bộ, có thể dùng trong vòng lặp, có thể tạo ra một Streams mới vì có thể set cho yield là vô hạn ^^, … Còn rất nhiều nữa, nhưng mình xin dừng bài này ở đây thôi nhé. Mình sẽ viết tiếp ở bài sau.

4. Tham khảo

# Understanding Generators in ES6 JavaScript with Examples
# function*
# Iterators and generators

Tagged , ,
0 0 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments