본문 바로가기

프로그래밍/js

ES lessons 12. 제너레이터

2023.04.09

자바스크립트 스터디 12회차

공부 사이트: https://poiemaweb.com/

 

12. 제너레이터

ES6에서 도입된 제너레이터 함수는 이터러블을 생성하는 함수이다.

→ 이터레이션 프로토콜을 준수하여 이터러블을 생성하는 방식보다 간편하게 이터러블을 구현할 수 있다. 

→ 비동기 처리에 유용하게 사용된다.

 

 

1) 제너레이터

// 이터레이션 프로토콜을 구현하여 무한 이터러블을 생성하는 함수
const createInfinityByIteration = function() {
	let i = 0;
	return {
		[Symbol.iterator]() { return this; },
		next() {
			return { value: ++i };
		}
	};
};

for(const n of createInfinityByIteration ()){
	if (n > 5) break;
	console.log(n);
}
// 무한 이터러블을 생성하는 제너레이터 함수
function* createInfinityByGenerator() {
	let i = 0;
	while(true) { yield ++i; }
}

for(const n of createInfinityByGenerator()){
	if (n > 5) break;
	console.log(n);
}

 

일반 함수 호출 : return문으로 반환 값 리턴

제너레이터 함수 호출 :  제너레이터 반환

제너레이터는 이터러블이면서 이터레이터인 객체

→ 제너레이터는 Symbol.iterator  메소드를 소유한 이터러블이며, 가지고 있는 next 메소드 호출 시 value/done 프로퍼티를 갖는 이터레이터 결과 객체를 반환하는 이터레이터이다.

 

function* counter() {
	for(const v of [1,2,3]) yield v;
}

let generatorObj = counter();

console.log(Symbol.iterator in generatorObj); //true
console.log('next' in generatorObj); //true

 

 

2) 제너레이터 함수의 정의

제너레이터 함수는 function* 키워드로 선언한다.

또한 하나 이상의 yield 문을 포함한다.

// 제너레이터 함수 선언식
function* genDecFunc(){
    yield 1;
}

// 제너레이터 함수 표현식
const genExpFunc() = function* (){
	yield 1;
};

// 제너레이터 메소드
const obj = {
	* generatorObjMethod(){
		yield 1;
	}
};

// 제너레이터 클래스 메소드
class MyClass{
	* generatorClsMethod(){
		yield 1;
	}
}

 

 

3) 제너레이터 함수의 호출

제너레이터 함수를 호출하면 함수 코드 블록이 실행되는 것이 아니라, 제너레이터 객체를 반환한다.

function* counter() {
  console.log('TEST 1');
  yield 1;                // 첫번째 next 메소드 호출 시 여기까지 실행
  console.log('TEST 2');
  yield 2;                // 두번째 next 메소드 호출 시 여기까지 실행
  console.log('TEST 3');
  yield 3;                // 세번째 next 메소드 호출 시 여기까지 실행
  console.log('TEST 4'); // 네번째 next 메소드 호출 시 여기까지 실행
}

// 제너레이터 함수 호출 시 제너레이터 객체 반환 
const generatorObj = counter();

// 첫 번째 next 메소드 호출 시 첫 yield문까지 실행되고 일시 중단
// 함수 내 모든 yield문이 실행되면 done 프로퍼티 값이 true가 됨.
console.log(generatorObj.next());

 

 

4) 제너레이터의 활용

① 이터레이션 프로토콜을 준수하여 이터러블을 생성하는 방식보다 간편하게 이터러블 구현이 가능하다.

 

② 제너레이터를 사용하여 비동기 처리를 동기 처리처럼 구현할 수 있다.

→ 비동기 처리 함수가 처리 결과를 반환하도록 구현 가능

// 원격 api 호출하는 fetch 함수 사용 위해 node-fetch 패키지 불러옴
const fetch = require('node-fetch');

function getUser(genObj, username){
	fetch(`https://api.github.com/users/${username}`)
		.then(res => res.json())
		// 제너레이터 객체에 비동기 처리 결과를 전달
		.then(user =>genObj.next(user.name));
}

// 제너레이터 객체 g
const g = (function* () {
	let user;
	// 비동기 처리 함수가 결과를 반환함. (비동기 처리 중 순서 보장 가능)
	user = yield getUser(g, 'jeresig');
	console.log(user);
	
	user = yield getUser(g, 'ahejlsberg');
	console.log(user);
}());

// 제너레이터 함수 시작
g.next();

[코드 설명]

- `https://api.github.com/users/사용자이름`  으로 요청 시 깃허브 user 정보가 들어있는 json을 제공- 비동기 처리가 완료되면 next 메소드 통해 제너레이터 객체에 비동기 처리 결과를 전달- 제너레이터 객체에 전달된 비동기 처리 결과는 user 변수에 할당됨

 

※ 코드 실행 이슈: node-fetch 설치 시 v2로 설치해야 정상 작동한다.

npm install node-fetch@2

 


제너레이터를 통해 비동기처리를 동기 처리처럼 구현 가능하나, 코드가 꽤 복잡하기에..

ES7에서 async/awat이 도입되었다.

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

// Promise를 반환하는 함수 (비동기 처리 시, 콜백 패턴 대신 사용하는 객체)
function getUser(username){
	return fetch(`https://api.github.com/users/${username}`)
		.then(res => res.json())
		.then(user => user.name);;
}

async function getUserAll() {
  let user;
  user = await getUser('jeresig');
  console.log(user);

  user = await getUser('ahejlsberg');
  console.log(user);
}

getUserAll();

 

'프로그래밍 > js' 카테고리의 다른 글

ES lessons 13-14. Babel, Webpack  (0) 2023.04.09
ES lessons 11. 이터레이션 & for of문  (0) 2023.04.05
ES6 lessons 10. 심볼  (0) 2023.04.03
ES6 lessons 9. 프로미스  (0) 2023.04.03
ES6 lessons 8. 모듈  (0) 2023.04.03