함수의 실행을 중간에 멈췄다가 재개할 수 있는 기능
즉, 다른 작업을 하다가 다시 돌아와서 next()
해주면 진행이 멈췄던 부분부터 이어서 실행
Redux-saga
에서 활발히 사용
function*
yield
내부에서 사용
함수의 실행을 멈출 수 있음
예시 코드
function* fn(){
yield 1
yield 2
yield 3
return "finish"
}
let a = fn() // Generator 함수 실행
실행을 처리하는 특별 객체
Generator 실행 시 → Generator 객체가 반환됨(코드 실행 x)
가지고 있는 메소드
next()
, return()
, throw()
가장 가까운
yield
문을 만날 때까지 실행 & 데이터 객체를 반환
value
&done
프로퍼티를 가짐
value
yield
오른쪽에 있는 값undefined
done
함수 코드가 끝났는지 나타냄
실행 끝 → true
아니면 → false
function* fn(){
console.log(1)
yield 1
console.log(2)
yield 2
console.log(3)
console.log(4)
yield 3
return "finish"
}
let a = fn()
console.log(a) // Generator 함수 실행
// Generator 객체만 반환. 함수 본문 코드: 실행x.
console.log(a.next()) // 첫 번째 yield 까지 실행
// 1
// {value: 1, done: false}
console.log(a.next()) // 두 번째 yield 까지 실행
// 2
// {value: 2, done: false}
console.log(a.next()) // 세 번째 yield 까지 실행
// 3
// 4
// {value: 3, done: false}
console.log(a.next()) // return까지 실행
// {value: "finish", done: true}
console.log(a.next())
// {value: undefined, done: true}
// value는 표현 할 것이 없음. done은 계속 true
외부로부터 값을 입력 받을 수 있다.
function* fn() {
let num1 = yield "첫 번째 숫자를 입력해주세요."
console.log(num1)
let num2 = yield "두 번째 숫자를 입력해주세요."
console.log(num2)
return num1 + num2
}
const a = fn()
console.log(a.next())
// 첫 번째 yield에서 멈춤.
// value는 yield 오른쪽에 있는 값.
// 결과 : {value: "첫 번째 숫자를 입력해주세요.", done: false}
console.log(a.next(2))
// 인수 넣음. 인수로 넣은 값은 num1에 저장됨.
// 그 다음 콘솔로 보여짐.
// 두 번째 yield에서 멈춤.
// 결과 : 2 {value: "두 번째 숫자를 입력해주세요.", done: false}
console.log(a.next(4))
// num2에 인수 값 4가 들어감.
// console.log로 보여짐.
// 더이상 yield 없음 -> done이 true가 됨.
// value는 두 숫자를 더한 값이 됨.
// 결과 : 4 {value: 6, done: true}
호출 즉시
done
속성 값은true
& 전달하는 인수가 있으면value
값이 됨.
이후 next
실행 시
value
→ 못 얻음done
→ true
예시 코드
function* fn(){
console.log(1)
yield 1
console.log(2)
yield 2
console.log(3)
console.log(4)
yield 3
return "finish"
}
let a = fn()
console.log(a.next())
// 1
// {value: 1, done: false}
console.log(a.next())
// 2
// {value: 2, done: false}
console.log(a.return('END'))
// {value: "END", done: true}
console.log(a.next())
// {value: undefined, done: true}
호출 즉시
done
속성 값은true
&catch
문 내부 실행
이후 next
실행 시
value
→ 못 얻음done
→ true
예시 코드
function* fn(){
// 예외 처리를 위해서 try cath 문으로 코드를 감쌈.
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)
}
}
let a = fn()
console.log(a.next())
// 1
// {value: 1, done: false}
console.log(a.next())
// 2
// {value: 2, done: false}
console.log(a.throw(new Error('err')))
// Error: err (에러 로그 찍힘)
// {value: undefined, done: true}
console.log(a.next())
// {value: undefined, done: true}
// value: undefined -> 아무 값도 받을 수 없음
// done: true -> 이미 끝남
Generator
는iterator
이면서iterable
이다.
반복이 가능하다
Symbol.iterator
메서드가 구현되어 있어야 함.Symbol.iterator
는 iterator
를 반환해야 함.iterable
(반복 가능한) 객체인 것Symbol.iterator
가 있고 이 메서드가 반환하는 값이 iterator
이므로 iterable
하다고 할 수 있음.
배열도 반복 가능
확인
방법
배열의 prototype
을 보면 Symbol.iterator
가 있음. 이걸 활용.
예시 코드
const arr =[1,2,3,4,5]
let it = arr[Symbol.iterator](); // it 변수 안에 array가 가진 Symbol.iterator 메소드를 실행한 값을 넣음.
console.log(it.next()) // {value: 1, done: false}
console.log(it.next()) // {value: 2, done: false}
console.log(it.next()) // {value: 3, done: false}
console.log(it.next()) // {value: 4, done: false}
console.log(it.next()) // {value: 5, done: false}
console.log(it.next()) // {value: undefined, done: true}
이유
Generator
에 Symbol.iterator
메소드를 실행한 값 === 자기 자신
function* fn(){
yield 4
yield 5
yield 6
}
const a = fn()
console.log(a[Symbol.iterator]()===a) // true
// 의미: Generator에 Symbol.iterator메소드를 실행한 값이 자기 자신이다.
const str = 'hello';
// Symbol.iterator 유무 확인
console.log(str[Symbol.iterator]) // 존재: ƒ [Symbol.iterator]() {}
// next() 실행
const xx = str[Symbol.iterator]();
console.log(xx.next()) // {value: "h", done: false}
console.log(xx.next()) // {value: "e", done: false}
console.log(xx.next()) // {value: "l", done: false}
console.log(xx.next()) // {value: "l", done: false}
console.log(xx.next()) // {value: "o", done: false}
console.log(xx.next()) // {value: undefined, done: true}
for .. of
가 시작이 되면 Symbol.iterator
를 호출.
만약 없으면 → 에러 발생
반환된 iterator
에 next
메소드를 호출하면서 done
이 true
가 될 때까지 반복
배열
const arr =[1,2,3,4,5]
for(let num of arr){
console.log(num)
}
/*
1
2
3
4
5
*/
Generator
function* fn(){
yield 4
yield 5
yield 6
}
const a = fn()
for(let num of a){
console.log(num)
}
/*
4
5
6
*/
문자열
const str = 'hello';
const xx = str[Symbol.iterator]();
// for..of 확인
for(let s of xx){
console.log(s)
}
/*
h
e
l
l
o
*/
Symbol.iterator
를 호출한 결과
next
메서드가 있어야 함.
next
메서드 반환: value
와 done
속성을 가진 객체
작업이 끝나면 → done
은 true
필요한 값만 그때그때 생성
무한 반복자(while (true)
문) 사용해도 브라우저 안 뻗음
이유
next
를 호출 할 때마다 값을 줘서.
예시 코드
function* fn(){
let index = 0;
while(true){
yield index++
}
}
const a = fn()
console.log(a.next()) // {value: 0, done: false}
console.log(a.next()) // {value: 1, done: false}
console.log(a.next()) // {value: 2, done: false}
console.log(a.next()) // {value: 3, done: false}
설명
Generator
함수를 사용하지 않았다면 break
없는 while true
문 → 사용하면 안됨.
일반적인 함수
값 구할 때, 모든 값을 미리 계산해놓고 유지 해야 함. (쓸지 안 쓸지 정해지지 않은 상황에서도)
Generator
필요한 순간까지 계산을 미룰 수 있음.
function* gen1() {
yield "w"
yield "o"
yield "r"
yield "l"
yield "d"
}
function* gen2() {
yield "Hello"
yield* gen1() // yield*을 하고 다른 제너레이터 함수(gen1())를 호출
yield "!"
}
console.log(...gen2());
// [결과] Hello w o r l d !
...
(구조 분해 할당)done
이 true
가 될 때까지 값을 펼쳐주는 역할
다른
generator
또는 이터러블(iterable
) 객체에yield
를 위임할 때 사용
즉, 반복 가능한 모든 객체가 올 수 있음.
참고