[javascript] 제너레이터 (generator)

죠현졍·2022년 7월 17일
0

TIL

목록 보기
5/6

함수

JavaScript에서 기본적인 구성 블록 중의 하나

작업을 수행하거나 값을 계산하는 문장 집합 같은 자바스크립트 절차

함수를 사용하려면 함수를 호출하고자 하는 범위 내에서 함수를 정의해야함

generator

하나의 값(혹은 0개)을 반환하는 일반 함수와 달리, 필요에 따라 여러개의 값을 하나씩 반환(yield)할 수 있는 함수

⇒ 제너레이터와 이터러블 객체를 함께 사용하면 손쉽게 데이터 스트림을 만들 수 있다.

*️⃣ generator 함수

제너레이터를 만들기 위해서는 제너레이터 함수라는 특별한 문법 구조가 필요함

function* gen(){
	yield 1;
	yield 2;
	yield 3;
}

let genFunc = gen();
console.log(genFunc); // [object Generator]

제너레이터 함수는 일반 함수와 동작 방식이 다르다.

제너레이터 함수 호출 시, 코드가 실행되지 않고 실행을 처리하는 특별 객체인 제너레이터 객체가 반환된다.

// 제너레이터 함수 선언문
function* genDecFunc(){
	yield 1;
}
// 제너레이터 함수 표현식
const genExpFunc = function*(){
	yield 1;
}
// 제너레이터 메서드
const obj = {
	* genObjMethod(){
		yield 1;
	}
};
// 제너레이터 클래스 메서드
class MyClass {
	* genClsMethod(){
		yield 1;
	}
}

생성 시 주의 사항

1️⃣ 애스터리스크(*)의 위치는 function 키워드와 함수 이름 사이라면 어디든지 상관없다.

다음 예제의 제너레이터 함수는 모두 유효하다. 하지만, 일관성 유지를 위해 function 키워드 바로 뒤에 붙이는 것을 권장한다.

function* genFunc(){ yield 1; }
function * genFunc(){ yield 1; }
function *genFunc(){ yield 1; }
function*genFunc(){ yield 1; }

2️⃣ 화살표 함수로 정의할 수 없다.

const genArrowFunc = * () => {
	yield 1;
}; // SyntacError: Unexpected token '*'

3️⃣ 제너레이터 함수는 new 연산자와 함께 생성자 함수로 호출할 수 없다.

function* getFunc(){
	yield 1;
}
new genFunc(); // TypeError: genFunc is not constructor

🎁 generator 객체

제너레이터 함수가 반환한 제너레이터 객체는 이터러블이면서 동시에 이터레이터다.

for..of , 전개문법([…array])사용 가능

function* genFunc(){
	yield 1;
	yield 2;
	yield 3;
}

const gen = genFunc();
console.log(Symbol.iterator in gen); // true
console.log('next' in gen); // true

제너레이터 객체는 next를 갖는 이터레이터지만 이터레이터에는 없는 throw, return 메서드를 갖는다.

➡️ next

제너레이터의 주요 메서드

호출 시 가장 가까운 yield 문을 만날 때까지 실행이 지속된다.

yield 문을 만나게 되면 value를 반환한다. (value 생략 시 undefined)

⇒ yield까지 실행하고 value 반환

  • value : 산출 값
  • done(boolean) : 함수 코드 실행의 종료 여부

↩️ return

return 메서드를 호출하면 인수로 전달받은 값을 value 프로퍼티 값으로, true를 done 프로퍼티 값으로 갖는 리터레이터 리정트 객체를 반환한다.

function* genFunc(){
	try{
		yield 1;
		yield 2;
		yield 3;
	}catch(e){
		console.log(e);
	}
}

const gen = genFunc();
console.log(gen.next()); // {value: 1, done: false}
console.log(gen.next()); // {value: 2, done: false}
console.log(gen.return('end')); // {value: 'end', done: true}

⤵️ throw

thorw메서드를 호출하면 인수로 전달받은 에러를 발생시키고 undefined를 value 프로퍼티 값으로, true를 done 프로퍼티 값으로 갖는 이터레이터 리절트 객체를 반환한다.

🔧 제너레이터의 활용

이터러블 구현

제너레이터 함수를 사용하면 이터레이션 프로토콜을 준수해 이터러블을 생성하는 방식보다 간단히 이터러블을 구현할 수 있다.

const infiniteFibo = (function(){
	let [prev, curr] = [0,1];
	return {
		[Symbol.iterator]() {return this;},
		next() {
			[prev, curr] = [curr, prev + curr];
			return { value: curr }; // 무한 이터러블이라 done 생략
		}
	}
}());

for(const num of infiniteFibo){
	if(num > 10000) break;
	console.log(num);
}
const infiniteFibo = (function* (){
	let [prev, curr] = [0,1];
	while(true){
		[prev, curr] = [curr, prev + curr];
		yield curr;
	}
}());

for(const num of infiniteFibo){
	if(num > 10000) break;
	console.log(num);
}

비동기 처리

제너레이터 함수는 next와 yield 표현식을 통해 함수 호출자와 함수의 상태를 주고받을 수 있다. 이러한 특성을 활용하면 프로미스를 사용한 비동기 처리를 동기 처리처럼 구현할 수 있다.

const fetch = require('node-fetch');

const async = genFunc => {
	const gen = gentFunc(); // 2
	const onResolved = arg = > {
		const result = gen.next(arg); // 5
		return result.done ? 
			result.value : // 9
			result.value.then(res => onResolved(res)); // 7
	}
	return onResolved; // 3
};

(async(function* fetchTodo() { // 1
	const url = '';
	const response = yield fetch(url); // 6
	const todo = yield response.json(); // 8
	console.log(todo);
})()); // 4

📚 Reference

https://im-developer.tistory.com/193

https://ko.javascript.info/generators#ref-1725

0개의 댓글