제너레이터 함수의 기본개념 :
제너레이터는 함수의 실행을 중간에 멈췄다가 중간에 재개할 수 있는 독특한 기능이다. 함수 옆에 *를 붙여서 사용을 한다.
함수 내부에 yield키워드를 붙이면 그곳에서 함수를 멈출 수 있다. 아래의 코드를 보자. 중간중간에 yield가 있다.
function* fn() {
console.log(1)
yield 1;
console.log(2)
yield 2;
console.log(3)
console.log(4)
yield 3;
return "finish";
}
const a = fn();
제너레이터 함수의 메서드 next() :
제너레이터에는 총 3가지 메서드가 있다. next, return, throw.이 중 next메서드를 먼저 알아보자.
위의 코드를 실행하면, 맨 처음에는 이 제너레이터 함수 자체를 반환한다.
그런데, a.next()라는 메서드를 통해서 제너레이터 함수를 호출하면 가장 가까운 yield문을 만나기 전까지 실행하고나서 멈춘다.
그 결과 맨 처음에는 yield1까지 실행되기 때문에
1
이라는 값이 출력된다. 그리고
{value:1, done: false}
라는 값이 반환되는데,
여기서 value는 yield의 오른쪽에 있는 값을 말한다. 만약 yield의 값을 생략하면 undefined라고 나올 것이다. done은 이름 그대로 함수코드가 끝났는지를 판단한다. 아직 제너레이터 함수가 끝나지 않았기 때문에, done은 false라고 나온다.
a.next()를 통해서 return 'finish'까지 도달하고나면 done은 true가 된다.
제너레이터 함수의 메서드 return():
제너레이터 함수는 next메서드 말고도, ruturn이라는 메서드가 있다.
위에서 작성했던 코드를 다시 실행을 하는데, 1과 2까지만 실행을 하고서 a.return('END')를 실행하면
{value: 'END', done: true}
로 함수가 바로 끝이 난다. 여기서 다시 a.next()를 실행하면
{value: undefined, done: true}
처럼 함수 자체가 이미 끝이난 것을 알 수 있다.
제너레이터 함수의 메서드 throw():
throw도 마찬가지로 done을 true로 바꿔준다. 위의 코드를 예외처리해주기 위해서 try, catch문으로 바꿔보자.
function* fn() {
try{
console.log(1)
yield 1;
console.log(2)
yield 2;
console.log(3)
console.log(4)
yield 3;
return "finish";
} catch(e){
console.log(e);
}
}
const a = fn();
그런다음 이 함수를 a.throw(new Error('err'))메서드로 실행을 하면 함수 안에 있던 catch문의 내용이 실행된다. 그리고 나서 값을 보면 {value: undefined, done: true}
가 출력된다. done이 끝난 것을 알 수 있다.
제너레이터는 iterable이다 :
제너레이터는 iterable이다. 이 말은 반복이 가능하다는 말이다. 이것이 가능하기 위해서는 몇가지 조건이 있다.
일단 메서드 symbol.iterator가 구현되어 있어야 한다. 그리고 이 메서드로 호출한 결과는 iterator를 반환해야 한다.
우리가 몰랐을 뿐이지, 일상적으로 사용하는 배열도 이 symbol이라는 메서드를 가지고 있다.
const arr = [1,2,3,4,5]
const it = arr[Symbol.iterator]();
it.next()를 해보면 아까 제너레이터에서 봤던 그 동작을 동일하게 볼 수 있다. 즉, 이 배열은 반복가능한 객체라는 말이다.
문자열도 동일하다.
const str = 'hello';
라는 문자열이 있다. 이 문자열에 대하여 symbol.iterator를 실행하면, 우리가 제너레이터에서 봤던 반환값을 그대로 볼 수 있다.
str[Symbol.iterator]
const xx = str[Symbol.iterator]()
xx.next();
{value: 'h', done: false}
{value: 'e', done: false}
{value: 'l', done: false}
{value: 'l', done: false}
{value: 'o', done: false}
이것을 통해서 문자열 또한 iterable이라는 것을 알 수 있다.
next에 인수전달하기 :
function* fn() {
const num1 = yield "첫번째 숫자를 입력해주세요";
console.log(num1);
const num2 = yield "두번째 숫자를 입력해주세요";
console.log(num2);
return num1 + num2;
}
const a = fn();
이 상태에서 a.next()를 실행하면
{value: '첫번째 숫자를 입력해주세요', done: false}
라는 값이 반환된다.
그 다음으로 next에 인수를 넣어보자. a.next(2)이렇게 값을 입력하면,
2
{value: '두번째 숫자를 입력해주세요', done: false}
라는 값이 반환된다.
그리고 a.next(4)를 입력하면,
{value: 6, done: true}
라는 값이 반환된다.
이런식으로 제너레이터는 외부에서 값을 받을 수 있다.
제너레이터는 값을 미리 만들어두지 않는다 :
function* fn(){
let index = 0;
while (true) {
yield index++;
}
}
const a = fn();
메모리 관리 측면에서 효율적이다. 필요한 순간에만 연산해서 값을 주기 때문에 이런 코드도 가능하다. while문을 true로 해놓고 돌려도 브라우저가 꺼지지 않는다. a.next()를 통해서 호출할 때만 값이 증가하기 떄문이다.
여기까지 제너레이터 함수에 대해서 알아보았다. 해당글은 코딩앙마님의 제너레이터 강의를 참고하여 작성한 글이다. 한번 듣고나니 어느정도 개념이 머릿속에 들어온다!
'Javascript' 카테고리의 다른 글
[자바스크립트] 프로토타입이란 무엇일까? (0) | 2021.10.09 |
---|---|
[자바스크립트] class 문법 이해하기 (0) | 2021.10.08 |
[자바스크립트] call, apply이란 무엇인가? 차이점은 무엇인가? (0) | 2021.10.04 |
[자바스크립트] 배열과 객체의 값을 복사하는 방법 / spread operator (0) | 2021.10.04 |
[자바스크립트] tagged literals 사용방법 (0) | 2021.10.04 |