[Generator 함수를 이해해보자 (이론편).1/3] 3. 제네레이터 함수의 "정의,What"와 "작동원리,How"는 무엇인가?

calm·2019년 3월 2일
3
post-thumbnail

[해당 이미지는 다음 링크에서 사용했습니다 : http://welllin.net/javascript-es6-generator/]

#제네레이터 함수 #제네레이터 함수는 무엇인가 #yield란 #제네레이터함수 작동 #Run-to-completion

여기서 잠깐!

이 블로그는 제네레이터 함수 공부하며 참고한 블로그 내용을 재 가공해 작성 한 글입니다.
중복되는 용어와 해당 부분에 대해 링크를 정리했습니다.궁금하신 분은 주소를 클릭하면 원문을 읽을 수 있습니다.

시작하는 글

지난 글 이론편에서 “어디에” 제네레이터 함수를 써야하는가?를 다뤘습니다.

오늘 우리는 제네레이터 함수가 무엇이고 어떻게 동작하는지에 대해 하나씩 살펴볼 것 입니다.

구성

블로그를 이론적인 부분과 코드편으로 나눠 작성됩니다. 현재 이론편을 다루고 있습니다.

(각각의 모든 글 하단에 참고&인용한 블로그 링크를 추가했습니다.)

목 차

[1]. “왜” 제네레이터 함수를 써야 하는가?
[2]. “어디에” 제네레이터 함수를 써야하는가?
[3]. 제네레이터 함수의 "정의"와 "작동원리"는 무엇인가?

세부 목차

[3] 제네레이터 함수의 "정의"와 "작동원리"는 무엇인가

  1. 제네레이터 함수란 무엇인가?
  • 1-1. 비유와 정의
  • 1-2. 일반함수와 제네레이터 함수의 차이점
  • 1-2-1. Run-to-completion과 coroutine(코루틴)
  • 1-2-2. 결괏값 : Value VS 이터레이터(Iterator)
  • 1-2-3. 값을 유지한다.
  • 1-3. 작성 방법
  • 1-4. 함수 작동순서

[다음편에 이어서]
2. 제네레이터는 어떻게 작동할까?

  • 2-1. yield를 어떻게 바라볼까?
  • 2-2. yield와 next()의 관계는 무엇인가?

1-1. 비유와 정의

Generators are a special class of functions that simplify the task of writing iterators.
A generator is a function that produces a sequence of results instead of a single value.
i.e you generate ​a series of values.

consider playing a game and suddenly mom calls for some work. You pause the game, help her, then resume playing again.
It is the same with generators.

제네레이터 함수를 설명하는 비유 입니다.

이런 경험 있을 겁니다.
게임을 하는 도중에 어머니께서 잠시 부릅니다. "pause"버튼을 누르고 게임을 도중에 멈춥니다. 일이 끝나고 방에 돌아와 "start"버튼을 눌러 잠시 중단한 게임을 이어 갑니다.

playing a game

제네레이터 함수가 실행됩니다.

and suddenly mom calls for some work. 
You pause the game, 

"잠시" 종료됩니다.

help her,

제네레이터 함수가 잠시 종료되는 동안 다른 일(help her)을 할 수 있다는 의미입니다.

then resume playing again. 

다시 실행됩니다.

다음은 MDN의 제네레이터 설명입니다.

function 선언 (끝에 별표가 있는 function keyword) 은 generator function 을 정의하는데, 이 함수는 Generator 객체를 반환합니다.
Generator 객체는 generator function 으로부터 반환된 값이며 반복자와 반복자 프로토콜을 준수합니다.
generator function 은 GeneratorFunction 생성자와 function
expression 을 사용해서 정의할 수 있습니다.

“반복자와 반복자 프로토콜을 준수합니다. “

모든 객체는 반복자 프로토콜을 준수하지 않습니다. 이미 빌트-인 특성으로 프로토콜을 따르는 객체들은 값을 하나씩 생성할 수 있습니다. 예를 들어 배열이 for..of 가 가능한 이유가 그런 것 입니다.

바꿔 말하면,
만약 객체가 그것을 따르면 값을 하나씩 하나씩 갖게 반환할 수 있게 됩니다.

[Symbol.iterator]()메서드로 객체를 호출하면 결과값으로 이터레이터 객체을 반환하며 이터레이터 프로토콜을 따릅니다.

이터레이터와, 이터러블, 이터레이터 프로토콜은 다른 블로그에서 설명하겠습니다.

제네레이터 함수는 동시에 이터레이터 특성을 가지고 있습니다.

[여기서 잠깐!]

제네레이터 함수가 반환하는 객체는 곧 이터레이터 객체 자신 이라고 라고 말한 적이 있습니다. 그 부분에 대해 코드로 설명 하려고 합니다.

function* generatorFunction() {
  yield 42;
}

const function_generator = generatorFunction();

console.log(function_generator == function_generator[Symbol.iterator]());

내용은 이렇습니다.

const function_generator = generatorFunction();

제네레이터 함수 generatorFunction가 반환하는 객체, 이터레이터를 변수 function_generator에 대입합니다.

function_generator[Symbol.iterator]()

[Symbol.iterator]()메서드는 이터레이터 프로토콜을 따르게 만들어 이터레이터 객체를 출력합니다.

console.log(function_generator == function_generator[Symbol.iterator]());

만약 두 값이 같은 객체(이터레이터를) 출력한다면 제네레이터와 이터레이터는 같다 라고 말할 수 있습니다.

console.log(function_generator == function_generator[Symbol.iterator]());

//결과값 true

제네레이터 함수가 반환하는 객체는 곧 이터레이터 객체 자신이며, 제네레이터를 실행하는 것은 곧 이터레이터를 실행하는 겁니다.

1-2. 일반함수와 제네레이터 함수의 차이점

제네레이터 함수를 설명하기 전에 무엇이 일반함수와 차이점이 있는지 알아봅시다.

[1]. Run-to-completion과 coroutine(코루틴)

일반함수는 매 실행마다 자신의 (함수)바디를 "모두" 실행하며 중간에 종료 할 수 없지만,
제네레이터는 yield를 중심으로 "실행"과 "종료"를 번갈아가며 작동합니다.

자바스크립트는 Run-to-completion 특징이 있습니다.

Run-to-completion scheduling is a scheduling model in which each task runs until it either finishes, or explicitly yields control back to the scheduler
-위키백과 중-

Run-to-completion은 한번 실행한 task는 무조건 종료 되어야 다름 task가 실행되는 특징입니다.

예를 들어 a task 가 있고 b task가 있다고 가정할때, a task가 실행되고 종료 되서야 b task가 실행됩니다.

a 테스크 실행 중에 b 테스크는 실행될 수 없고 무조건 a테스크가 종료되야 b테스크가 실행된다는 것입니다.

이벤트 루프에서 task는 이벤트 큐에 적재된 순서대로만 작동합니다.

즉 중간에 인터럽이 될 수 없고 중간에 종료된다는 것이 있을 수 없습니다.

하지만 이런 특성을 제네레이터 함수는 그렇지 않습니다.제네레이터 함수는 코루틴 특성("왜 제네레이터 함수를 써야하는가")을 가지고 있어서 yield를 기준으로 함수 실행 중 실행-종료 될 수 있습니다.

[2]. 결괏값 : Value VS 이터레이터(Iterator)

일반함수는 값을 return 하지만 제네레이터는 이터레이터(Iterator) 객체를 반환합니다.

일반함수와 제네레이터 함수가 반환하는 변수 str값을 비교해봅시다.

//일반함수
function normal_func() {
    var str = "Hello normal_func";
    return str;
}

//제네레이터 함수
function* generator_func() {
    var str = "Hello generator_func";
    console.log(str);
    return str;
}

console.log(normal_func()); // "Hello normal_func"
console.log(generator_func()); // 제네레이터 generator_func 객체"

일반함수 normal_func를 호출해 console.log로 확인하면 문자열 "Hello normal_func"을 값을 볼 수 있습니다.

반면 제네레이트 함수 generator_func를 호출하면 제네레이터 객체(이터레이터 객체)가 확인됩니다. 제네레이터 함수는 next()메서드를 호출해야 실제로 실행됩니다. 그런 이유로 ""입니다.

[3]. 값을 유지한다.

일반 함수는 함수 자체가 종료가 되고 콜스택을 빠져 나오면서 함수바디에 설정한 값이 초기화 됩니다.즉 저장이 되지 않습니다.(클로저를 통해서 값을 유지하는 방법이 있습니다.)

제네레이터 함수는 괄호{...}안에서 수행하는 로직에 값이 계속 유지가 됩니다. 이에 대해 코드편 에서 이런 특징을 설명하겠습니다.

1-2. 작성 방법

function* name(arguments) {
   statements
}

function* : function 다음에 *(asterisk)이 을 넣습니다.
name : 함수 이름입니다.
statements : "실행-중지-실행-중지" 형태 내용이 들어갑니다.

제네레이터 함수가 반환하는 객체입니다.

{value : "value" ,  done: true|false}

//true : 더 이상 반환할 값이 없습니다 , 종료됐습니다.
//false : 아직 반환 할 값이 남았습니다.

value에는 값이 들어가고 이터레이터(Iterator)가 출력하는 값을 의미합니다.

1-3 . 함수 작동순서

제네레이터 함수를 만들어 봅시다.

function * generatorFunction() { 
  console.log('This will be executed first.');
  yield 'Hello, ';   
  console.log('I will be printed after the pause');  
  yield 'World!';
}
const generatorObject = generatorFunction(); 

console.log(generatorObject.next().value); 
console.log(generatorObject.next().value); 
console.log(generatorObject.next().value); 

// This will be executed first.
// Hello, 
// I will be printed after the pause
// World!
// undefined

generatorFunction 제네레이트 함수도 함수이기 때문에 코드 한줄 한줄을 실행합니다.

const generatorObject = generatorFunction(); 

제네레이터 함수 generatorFunction를 호출();하면 바로 실행되지 않습니다. 변수 generatorObject에 함수 generatorFunction가 반환하는 "이터레이터 객체"를 대입합니다.

이제 이터레이터 객체의 next()메서드를 호출해 generatorFunction함수에 정의한 값을 하나씩 출력합니다.

function * generatorFunction() { 
  console.log('This will be executed first.');
  yield 'Hello, '; // yield #1  
}

const generatorObject = generatorFunction(); 
console.log(generatorObject.next().value); // next()  

// This will be executed first.
// Hello, 

This will be executed first.console.log합니다.

  yield 'Hello, '; 

첫 번째 yield 입니다. generatorFunction함수는 Hello를 yield(넘겨주다)하고, (넘겨준) Hello를 console.log 합니다. 동시에 함수가 종료 됩니다.첫 번째 함수 종료입니다.

제네레이트 함수는 yield를 만나면 yield 표현식을 넘겨주고 실행을 (잠시) 종료합니다

다음은 MDN의 yield에 대한 설명입니다.

yield 키워드는 제너레이터 함수 (function* 또는 레거시 generator 함수)를 중지하거나 재개하는데 사용됩니다.

이어서 설명합니다.

function * generatorFunction() { 
  console.log('This will be executed first.');
  yield 'Hello, '; // yield #1   
  console.log('I will be printed after the pause');  
  yield 'World!'; // yield #2
}
const generatorObject = generatorFunction(); 

console.log(generatorObject.next().value); // next() #1
console.log(generatorObject.next().value); // next() #2

// This will be executed first.
// Hello, 
// I will be printed after the pause
// World!

I will be printed after the pauseconsole.log합니다.

  yield 'World! '; 

두 번째 yield 입니다. generatorFunction함수는 World!를 yield(넘겨주다)하고, (넘겨준) World!를 console.log 합니다. 동시에 함수가 종료 됩니다. 두 번째 함수 종료입니다.

function * generatorFunction() { 
  console.log('This will be executed first.');
  yield 'Hello, '; // yield #1   
  console.log('I will be printed after the pause');  
  yield 'World!'; // yield #2
}
const generatorObject = generatorFunction(); 

console.log(generatorObject.next().value); // next() #1
console.log(generatorObject.next().value); // next() #2
console.log(generatorObject.next().value); // next() #3

// This will be executed first.
// Hello, 
// I will be printed after the pause
// World!
// undefined
  undefined

함수는 undefined 를 출력하고 종료됩니다.

더 이상 yield 할 것이 없어 next( )메서드를 호출하면 undefined 가 출력됩니다.

즉. 제네레이터 함수는 yield로 선언한 값을 next()를 통해 반환합니다.

yield가 2번 으로 next()가 2번 작동됐습니다.
나중에 yield와 next()의 관계에 대해 정리하려는데, 항상 yield와 next()메서드 숫자가 서로 같게 매칭되지는 않습니다.

다음 편에는 제네레이터 함수의 정의와 작동원리를 계속 이어 설명하겠습니다.

참고&인용 레퍼런스 링크

제네레이터 함수 정의(MDN) :
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/function* ,
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Generator
,
: https://codeburst.io/understanding-generators-in-es6-javascript-with-examples-6728834016d5

제네레이터 함수 특징(MDN) :
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/function*

제네레이터 함수 작성방법
: https://codeburst.io/understanding-generators-in-es6-javascript-with-examples-6728834016d5
,
: https://medium.freecodecamp.org/yield-yield-how-generators-work-in-javascript-3086742684fc

yield(MDN) : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/yield

결괏값 : Value VS 이터레이터(Iterator) 코드:
제네레이터 객체와 이터레이터 객체 비교코드 :
https://thd0011.tistory.com/1

profile
공부한 내용을 기록합니다

0개의 댓글