그 외

[JS] 제네레이터와 비동기 이터레이션

swotato 2025. 9. 8. 17:31

2022. 11. 07 스터디 내용

참고

https://ko.javascript.info/

https://ko.javascript.info/generators-iterators

 

제너레이터와 비동기 이터레이션

 

ko.javascript.info

 


 

제너레이터 (Generator)

일반적인 함수는 하나의 값을 반환함. 제너레이터는 여러개의 값을 필요에 따라서 하나씩 반환 할 수 있도록 해줌.

* 비동기 처리에 유용하게 사용됨.

function* commonFunc() {
	return 1;
}

function* generatorFunc() {
	yield 1; // 첫번째 반환 값
	yield 2; // 두번째 반환 값
	return 3; // 세번째 반환 값
}

 

제너레이터 함수

제너레이터를 사용하기 위해서는 제너레이터 함수(function* 함수명(){...})가 필요함.

함수를 호출하면, 코드는 실행되지 않고 제너레이터 객체(이터러블)를 반환함.

* 화살표 함수를 사용할 수 없음

 

제너레이터 객체

제너레이터 객체는 next() 메서드를 가짐.

next( )는 가장 가까운 yield 문까지 코드를 실행하고, 객체를 반환함. 이 객체는 value, done 프로퍼티를 가짐.

  • value: 반환 값
  • done: 함수 코드가 끝났는지 여부 (true: 끝남 / false: 함수 진행중)
function* generatorFunc() {
	console.log("첫번째 호출");
	yield 1;
	console.log("두번째 호출");
	yield 2;
	console.log("세번째 호출");
	return 3; // 또는 yield 3;
}

let generator = generatorFunc();

generator.next(); //{value:1, done:false}
generator.next(); //{value:2, done:false}
generator.next(); //{value:3, done:true}

 

yield 키워드

결과를 제너레이터 밖으로 보내기도 하지만, 밖에서부터 안으로 받아올 수도 있음.

function* generatorFunc() {
  let first = yield "1 + 1 = ?";
	let secsond = yield "2 + 2 = ?";
}

let generator = generatorFunc();

let result1 = generator.next().value; 

let result2 = generator.next(2).value;

let result3 = generator.next(4).done;
function* generatorFunc() {
  let first = yield "1 + 1 = ?";
	let secsond = yield "2 + 2 = ?";
}

let generator = generatorFunc();

let result1 = generator.next().value; 
// result1 = "1 + 1 = ?"

let result2 = generator.next(2).value;
// first = 2
// result2 = "2 + 2 = ?"

let result3 = generator.next(4).done;
// second = 4
// result3 = true

 

이터러블 (iterable, 반복 가능한)

제너레이터 객체는 이터러블 객체이므로 for .. of 반복문전개 문법(…)을 사용할 수 있음.

(단, done:true일 때의 value값을 무시하므로 주의할 것)

function* generatorFunc() {
	yield 1;
	yield 2;
	return 3; // **
}

let generator = generatorFunc();

for(let value of generator) {
  console.log(value); //1, 2 출력
}

let arr = [...generatorFunc()] //1, 2
function* generatorFunc() {
	yield 1;
	yield 2;
	yield 3; // **
}

let generator = generatorFunc();

for(let value of generator) {
  console.log(value); //1, 2, 3 출력
}

let arr = [...generatorFunc()] //1, 2, 3

 

제너레이터 컴포지션

yield* 키워드를 사용하여 제너레이터 안에 제너레이터를 임베딩(embedding, composing) 할 수 있음.

실행 결과(산출값)를 바깥으로 전달함.

🔽 아래 코드는 중첩 제네레이터를 사용하는 것과 같은 역할을 함.

function* generateSequence(start, end) {
  for (let i = start; i <= end; i++)
			yield i;
}

function* generatePasswordCodes() {

  // 0..9
  yield* generateSequence(48, 57);

  // A..Z
  yield* generateSequence(65, 90);

  // a..z
  yield* generateSequence(97, 122);

}

let str = '';

for(let code of generatePasswordCodes()) {
  str += String.fromCharCode(code);
}

alert(str); // 0..9A..Za..z

 

예외처리

제너레이터객체.throw()를 사용해 에러를 전달할 수 있음.

에러에 대한 예외처리는 제너레이터 함수 내부, 또는 함수 호출 위치에서 할 수 있음.

🔽 제네레이터 함수 내에서 예외처리를 하는 경우

function* generatorFunc() {
	try {
		let first = yield "1 + 1 = ?";
    alert("yield에서 에러 발생하지 않음");
  } catch(e) {
    alert(e); // error message
  }
}

let generator = generatorFunc();
let result = generator.next().value; 

generator.throw(new Error("error message")); // 에러 발생

🔽 제네레이터 함수 호출 부분에서 예외처리를 하는 경우

function* generatorFunc() {
	let first = yield "1 + 1 = ?";
}

let generator = generatorFunc();
let result = generator.next().value;

try {
	**generator.throw(new Error("error message"))**; // 에러 발생
} catch(e) {
	alert(e); // error message
}

async 이터레이터와 제너레이터

비동기 이터레이터/비동기 제너레이터를 사용하여 비동기적으로 데이터를 받을 수 있음. 주로 네트워크 프로그래밍 등에 쓰임.

 

비동기 이터레이터 (Async Iterator)

일반 이터레이터와 비슷하지만, 다음과 같은 차이점이 있음.

  1. Symbol.iterator대신 Symbol.asyncIterator 를 사용해야함.
  2. next() 메서드는 반드시 Promise를 반환해야함.
  3. 비동기 이터레이터 객체를 사용한 반복문을 호출할 때에는, 반드시 await를 사용해야함.
  4. 비동기 이터레이터 객체는 전개 문법을 사용할 수 없음.

🔽 일반 이터러블 객체

let range = {
  from: 1,
  to: 5,

  [Symbol.iterator]() {
    return {
      current: this.from,
      last: this.to,

      next() {
        if (this.current <= this.last) {
          return { done: false, value: this.current++ };
        } else {
          return { done: true };
        }
      }
    };
  }
};

for(let value of range) {
  alert(value); // 1, 2, 3, 4, 5
}

🔽 비동기 이터레이터 객체

let range = {
  from: 1,
  to: 5,

  [Symbol.asyncIterator]() {
    return {
      current: this.from,
      last: this.to,

      async next() {
        await new Promise(resolve => setTimeout(resolve, 1000))

        if (this.current <= this.last) {
          return { done: false, value: this.current++ };
        } else {
          return { done: true };
        }
      }
    };
  }
};

(async () => {
  for await (let value of range) {
    alert(value); // 1,2,3,4,5
  }
})()

'그 외' 카테고리의 다른 글

[pgAdmin][Error] FATAL: connection requires a valid client certificate  (0) 2025.09.16
[langchain] document loader  (0) 2025.09.15
[JS] 프라미스와 async, await 정리  (0) 2025.09.08
[Git] 기초  (0) 2025.09.03