본문 바로가기
Programing/Javascript

[JavaScript] JavaScript Generators(자바스크립트 생성기)

by 멍멍돌이야 2022. 6. 30.
반응형

Introduction to JavaScript Generators

(JavaScript 생성기 소개)

 

 

JavaScript에서는 실행 후 완료 모델을 기반으로 일반 함수가 실행됩니다. 중간에 일시 중지할 수 없으며 일시 중지된 위치에서 계속됩니다. 

예를들면 다음과 같습니다.

function foo() {
    console.log('I');
    console.log('cannot');
    console.log('pause');
}

foo() 함수는 위에서 아래로 실행됩니다. foo()를 종료하는 유일한 방법은 foo()에서 반환하거나 오류를 발생시키는 것입니다. foo() 함수를 다시 호출하면 위에서 아래로 실행이 시작됩니다.

foo();

Output:

I
cannot
pause

ES6은 일반 함수(regular function)와 다른 새로운 종류의 함수인 함수 생성기(function generator) 또는 생성기를 도입했습니다.

 

생성기는 중간에 일시 중지한 다음 일시 중지된 위치에서 계속할 수 있습니다. 예를들면 다음과 같습니다.

function* generate() {
    console.log('invoked 1st time');
    yield 1;
    console.log('invoked 2nd time');
    yield 2;
}

generate() 함수를 자세히 살펴보겠습니다.

 

  • 첫째, function 키워드 뒤에 별표(*)가 표시됩니다. 별표(*)는 generate()가 일반 함수가 아니라 생성기임(generator)을 나타냅니다.
  • 둘째, yield 문은 값을 반환하고 함수 실행을 일시 중지합니다.

다음 코드는 generate() 생성기를 호출합니다.

let gen = generate();

generate() 생성기를 호출할 때:

  • 첫째, 콘솔에 아무것도 표시되지 않습니다. generate()가 일반 함수인 경우 몇 가지 메시지가 표시될 것으로 예상됩니다.
  • 둘째, generate()에서 반환된 값으로 무언가를 얻습니다.

 

콘솔에 반환된 값을 표시해 보겠습니다.

console.log(gen);

Output:

Object [Generator] {}

따라서 제너레이터는 호출될 때 본체를 실행하지 않고 generate 객체를 반환합니다.
Generator 객체는 done과 value라는 두 가지 속성을 가진 다른 객체를 반환합니다. 즉, Generator 객체는 반복 가능합니다.
다음은 Generator 객체에서 next() 메서드를 호출합니다.

let result = gen.next();
console.log(result);

Output:

invoked 1st time
{ value: 1, done: false }

보시다시피 Generator 객체는 1번째 줄에서 'invoked 1st time' 메시지를 출력하는 본문을 실행하고 2번째 줄에서 값 1을 반환합니다.

yield 문은 1을 반환하고 2행에서 생성기를 일시 중지합니다.

마찬가지로 다음 코드는 Generator의 next() 메서드를 두 번째로 호출합니다.

result = gen.next();
console.log(result);

Output:

invoked 2nd time
{ value: 2, done: false }

이번에는 Generator가 'invoked 2nd time' 메시지를 출력하고 2를 반환하는 3행에서 실행을 재개합니다.

다음은 제너레이터 객체의 next() 메서드를 세 번째로 호출합니다.

result = gen.next();
console.log(result);

Output:

{ value: undefined, done: true }

생성기는 반복 가능하므로 for...of 루프를 사용할 수 있습니다.

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

출력은 다음과 같습니다.

invoked 1st time
1
invoked 2nd time
2

 

More generator examples(생성기 예제)

다음 예는 생성기를 사용하여 끝없는 시퀀스를 생성하는 방법을 보여줍니다.

function* forever() {
    let index = 0;
    while (true) {
        yield index++;
    }
}

let f = forever();
console.log(f.next()); // 0
console.log(f.next()); // 1
console.log(f.next()); // 2

영원히 생성기의 next() 메서드를 호출할 때마다 0부터 시작하는 시퀀스의 다음 숫자를 반환합니다.

 

Using generators to implement iterators(반복자 구현)

반복자를 구현할 때 next() 메서드를 수동으로 정의해야 합니다. next() 메서드에서 현재 요소의 상태도 수동으로 저장해야 합니다.

생성기는 반복 가능하므로 반복기를 구현하기 위한 코드를 단순화하는 데 도움이 될 수 있습니다.

다음은 반복자 튜토리얼에서 생성된 시퀀스 반복자입니다.

class Sequence {
    constructor( start = 0, end = Infinity, interval = 1 ) {
        this.start = start;
        this.end = end;
        this.interval = interval;
    }
    [Symbol.iterator]() {
        let counter = 0;
        let nextIndex = this.start;
        return  {
            next: () => {
                if ( nextIndex < this.end ) {
                    let result = { value: nextIndex,  done: false }
                    nextIndex += this.interval;
                    counter++;
                    return result;
                }
                return { value: counter, done: true };
            }
        }
    }
}

다음은 생성기를 사용하는 새로운 Sequence 반복기입니다.

class Sequence {
    constructor( start = 0, end = Infinity, interval = 1 ) {
        this.start = start;
        this.end = end;
        this.interval = interval;
    }
    * [Symbol.iterator]() {
        for( let index = this.start; index <= this.end; index += this.interval ) {
            yield index;
        }
    }
}

보시다시피 Symbol.iterator 메서드는 생성기를 사용하여 훨씬 간단합니다.

다음 스크립트는 Sequence 반복자를 사용하여 1에서 10까지의 홀수 시퀀스를 생성합니다.

let oddNumbers = new Sequence(1, 10, 2);

for (const num of oddNumbers) {
    console.log(num);
}

Output:

1
3
5
7
9

 

Using a generator to implement the Bag data structure

(생성기를 사용하여 Bag 데이터 구조 구현하기)

 

Bag은 요소를 수집하고 요소를 반복할 수 있는 데이터 구조입니다. 항목 제거를 지원하지 않습니다.

다음 스크립트는 Bag 데이터 구조를 구현합니다.

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

let bag = new Bag();

bag.add(1);
bag.add(2);
bag.add(3);

for (let e of bag) {
    console.log(e);
}

Output:

1
2
3

 

Summary

  • 생성기는 생성기 함수 function* f(){}에 의해 생성됩니다.
  • 제너레이터는 호출되는 즉시 본체를 실행하지 않습니다.
  • 생성기는 중간에 일시 중지하고 일시 중지된 위치에서 실행을 다시 시작할 수 있습니다.
    • yield 문은 생성기 실행을 일시 중지하고 값을 반환합니다.
  • 생성기는 반복 가능하므로 for...of 루프와 함께 사용할 수 있습니다.

 

 

 

 

 

 

 

 

Reference: https://www.javascripttutorial.net/es6/javascript-generators/

 

 

 

728x90
반응형

댓글