yield의 반복
이전 게시글에서 next() 메서드로 호출될 때마다 yield 구문의 값을 반환한다고 했다.
yield가 반복되면 어떻게 될까? (이런 생각은 해보지도 않았는데 역시 진짜 개발자들은 다르군)
아래 코드는 yield를 반복하는 형태로 구성되었다.
let status = true;
function* sports() {
let count = 0;
while (staus) {
yield ++count;
};
};
const obj = sports();
console.log(obj.next()); // {value: 1, done: false}
console.log(obj.next()); // {value: 2, done: false}
status = false;
console.log(obj.next()) // // {value: undefined, done: true}
변수 count가 0으로 초기화된 상태로 시작한다. 처음 next()가 진행되면 sports 함수가 실행되고, status가 true이기 때문에 ++count = 1 값이 반환된다. 이 때 obj는 Generator 함수로 생성되고 반환된 이터레이터 오브젝트라서 {value: 1, done: false}로 출력된다. 다음 next()가 진행될 때도 동일하게 진행하되 ++count = 2 value:2가 출력된다.
status = false로 바꾸고 나면 yield ++count가 더 이상 실행되지 않는다. yield문이 없기 때문에 value: undefined를 반환하고, done: true가 된다. done: true가 되면 더 이상 이터레이터를 사용할 수 없다.
function* sports() {
return yield yield yield;
};
const obj = sports();
console.log(obj.next()); // {value: undefined, done: false}
console.log(obj.next(10)); // {value: 10, done: false}
console.log(obj.next(20)); // {value: 20, done: false}
console.log(obj.next(30)); // {value: 30, done: done}
위의 코드처럼 yield가 여러 개면 어떻게 될까?
(이런 생각은 해보지도 않았는데 역시 진짜 개발자들은 다르군2)
1번째 next()에는 yield 값이 없어 value가 undefined가 된다.
2번째 next()에는 파라미터 값이 10이 있다. 2번째 yield가 수행되면서 함수에 파라미터 값을 받을 변수가 없다면 그대로 파라미터로 넘겨준 값을 반환한다. 따라서 value는 10이 된다.
3번째 next() 역시 파라미터 값이 20이 있다. 3번째 yield가 수행되면서 동일하게 파라미터로 넘겨 받은 값을 반환해 value는 30이 된다.
4번째 next()에도 파라미터 값으로 30이 있지만, yield가 더는 없다. 이럴 때는 value는 똑같이 파라미터로 넘겨 받은 값을 반환해주되 return문을 작성해서 done: true가 된다. 만약 return문을 작성하지 않으면 value는 30이 아니라 undefined로 찍히게 된다.
yield의 분할 할당과 for... of 반복
대괄호 [] 안에 다수의 yield를 작성하면 어떻게 될까?
(이런 생각은 해보지도 않았는데 역시 진짜 개발자들은 다르군3)
위에서 했던 것처럼 yield를 다수 작성했다.
1번째 next()에서 yield를 만나지만 뒤에 적은 값이 없어 undefined가 나온다.
2번째 next()에서도 yield를 만나는데 이 때 값이 없어도 인수 10을 갖고 있어 그대로 10이 반환된다.
function* sports() {
return [yield yield];
};
const obj = sports();
console.log(obj.next()); // {value: undefined, done: false}
console.log(obj.next(10)); // {value: 10, done: false}
const last = obj.next(20);
console.log(last); // {value: [20], done: false}
console.log(last.value) // [20]
3번째 next()에서는 더 이상 처리할 yield가 없다.
이럴 때는 yield를 무시하고 대괄호 안에 인수를 넣어 그대로 반환한다.
20이 [yield] 안으로 그대로 들어가면서 [20]과 같은 value가 나오게 되는 것이다.
다음으로 for... of 구문으로 제너레이터를 반복 호출해보겠다.
function* sports(count) {
while (true) {
yield ++count;
};
};
for (let point of sports(10)) {
console.log(point);
if (point > 12) {
break;
};
}
// 11
// 12
// 13
처음 시작하면 sports(10)으로 제너레이터 오브젝트를 생성하고 10을 설정한다.
이 제너레이터 오브젝트를 저장할 변수가 별도로 없어서, 그냥 엔진 내부에 저장한다고 가정한다.
예컨대 const engine = sports(10) 이런 식으로...
++count로 point는 11이 된다. 11이 sports 함수에 다시 들어가면 12가 되고, 12가 들어가면 13이 출력된다.
이 때 point가 12보다 커진 13이 되었으므로 break되고 출력된 값은 모두 11, 12, 13 3개가 나오게 된다.
yield*의 표현식
[yield* 표현식] 이렇게 사용하는데 표현식에 따라 처리 방법이 다르다.
- yield*의 표현식이 배열일 때
function* sports() {
yield* [10, 20];
};
const obj = sports();
console.log(obj.next()); // {value: 10, done: false}
console.log(obj.next()); // {value: 20, done: false}
console.log(obj.next()); // {value: undefined, done: true}
위 코드와 같이 배열 안에 있는 엘리먼트를 순서대로 반환한다.
배열의 길이보다 next()의 횟수가 많아지면 똑같이 undefined, done: true를 반환한다.
- yield*의 표현식이 제너레이터 함수일 때
function* point(count) {
yield count + 5;
yield count + 10;
};
function* sports(value) {
yield* point(value)
yield value + 20;
};
const obj = sports(10);
console.log(obj.next()); // {value: 15, done: false}
console.log(obj.next()); // {value: 20, done: false}
console.log(obj.next()); // {value: 30, done: false}
yield*가 point(value) 형태를 띄고 있다.
파라미터를 그대로 전달해주면 count + 5로 15, count + 10으로 20이 된다.
맨 위의 yield 2개를 모두 사용했으니 3번째 next()는 point(value)가 아닌 그 아랫줄의 평범한 yield에 적용된다.
따라서 기존의 파라미터 10 + 20을 30이 출력된다.
- yield*의 표현식이 재귀 호출일 때
function* sports(point) {
yield point
yield* sports(point + 10);
};
const obj = sports(10);
console.log(obj.next()); // {value: 10, done: false}
console.log(obj.next()); // {value: 20, done: false}
console.log(obj.next()); // {value: 30, done: false}
1번째 next()를 실행하면 인수 10을 가지고 그대로 올라가 1번째 yield에서 10을 반환한다.
2번째 next()를 실행하면 똑같이 인수 10을 가지고 올라가 2번째 yield에 멈춘다. 이 때 이 yield는 어떤 파라미터를 반환하는 게 아니라 자기 자신을 호출하고 있다. 10 + 10 이라는 인수를 가지고 자기 자신을 호출하면 이번에도 1번째 yield에서 멈춰 20을 반환한다.
3번째 next()를 실행하면 2번째 yield에 멈춰야 한다. point(20) + 10으로 30 이라는 인수를 가지고 자기 자신을 호출하면, 또 1번째 yield에 멈춰 30을 반환한다.
한 번씩 yield point로 빠져나가는 것이 핵심이며 이 코드가 없다면 진짜 계속 자기 자신을 호출하게 된다.
제너레이터 오브젝트의 메소드
generatorObject.return()
- 파라미터(opt): 제너레이터로 넘겨줄 값
- 반환: return()의 파라미터 값
- 이터레이터를 종료한다. (next()를 더 이상 사용할 수 없음)
아래와 같이 1번째 next() 수행 시에는 ++count로 11이 출력된다.
하지만 2번째의 return() 수행 시에는 파라미터 값 70만 반환하고, 이터레이터를 종료한다.
그래서 3번째 next() 수행 시 이터레이터가 작동하지 않아 값을 undefined로 출력하고, done도 true가 된다.
function* sports(count) {
while (true) {
yield ++count;
};
};
const obj = sports(10);
console.log(obj.next()); // {value: 11, done: false}
console.log(obj.return(70)); // {value: 70, done: true}
console.log(obj.next(50)); // {value: undefined, done: true}
generatorObject.throw()
- 파라미터: 에러 메세지, Error 오브젝트
- 반환: {value: 에러 메세지, done: true}
- 에러를 의도적으로 발생시켜서 제너레이터 함수의 catch()문에서 에러를 받는다.
에러 발생이 실행하게 되어도 제너레이터는 종료되지 않고 다음 next()를 호출한다.
function* sports() {
try {
yield 10;
} catch (message) {
yield message;
};
yield 20;
};
const obj = sports();
console.log(obj.next()); // {value: 10, done: false}
console.log(obj.throw("에러 발생")); // {value: 에러 발생, done: false}
console.log(obj.next()); // {value: 20, done: false}
throw()와 throw 구문의 차이는 후자의 경우 종료한다는 것이다.
function* sports() {
throw "에러 발생";
yield 10;
};
const obj = sports();
try {
const result = obj.next();
} catch (message) {
console.log(message) // 에러 발생
};
console.log(obj.next()); // {value: undefined, done: true}
const result = obj.next()를 실행하면 제너레이터 함수 안에서 에러를 던진다.
에러를 캐치해 에러 발생이라는 문구를 내뱉고 다시 마지막 줄에서 next()를 호출하면 yield 10이 남아 있음에도 불구하고 제너레이터가 실행되지 않고 종료된다.
'👋🏻 JavaScript > 📖 자바스크립트 ES6+' 카테고리의 다른 글
[JS] 콜백 함수와 동기/비동기를 알아보자 (0) | 2023.07.22 |
---|---|
[JS] Symbol 오브젝트를 알아보자 (0) | 2023.06.24 |
[JS] Generator 오브젝트를 알아보자 : Generator 함수, Generator Function, yield, next() (0) | 2023.06.23 |
[JS] Math 오브젝트를 알아보자 (0) | 2023.06.22 |
[JS] Array 오브젝트의 이터레이터 오브젝트 생성을 알아보자 (0) | 2023.06.22 |