[JS] 비동기프로그래밍 - generator, iterator, async/await
Tags: javascript, generator, iterator, korean
Published: 2020-01-06 03:41:49

## Iterator

- 배열이나 유사한 자료구조 내부를 순회하는 객체이다
- iterable, iterator 두가지 iterable protocol이 존재

### [Iterable]

- 객체 멤버를 반복할 수 있는 객체
- 자바스크립트에서 객체가 iterable 하기 위해서는 `[@@iterator]` 메소드 구현 필요, object property에 `Symbol.iterator`를 추가해야 한다.


```javascript
        const iterable = new Object();
        
        // 객체는 오직 하나의 Symbol.iterator를 가질 수 있다
        
        iterable[Symbol.iterator] = function* () {
          yield 1;
          yield 2;
          yield 3;
        };
        
        console.log([...iterable]); // 1 2 3
        for(var value of iterable) {
            console.log(value); // 1 2 3
        }
```
- Array, String, Map, Set, TypedArray가 iterable을 기본적으로 가지고 있다.

### [Iterator]

- 객체를 next() 메서드로 순환 할 수 있는 객체
- next()메소드 규칙
    - arguments가 없다
    - next 메소드의 반환자는 done: boolean , value:any 를 포함하는 object 를 반환해야한다
    - next 메소드의 반복이 끝날때 done 은 true 를 반환해야 한다 그전에는 false


```javascript
    // iterator를 반환하는 함수들
    
    var arr = ['1', '2', '3'];
    console.log([...arr.entries()]);
    // [[0, "1"], [1, "2"], [2, "3"]];
    console.log([...arr.keys()]);
    // [0, 1, 2];
    console.log([...arr.values()]);
    // ["1", "2", "3"];
    
    var map = new Map([[1, '1'],[2, '2'],[3, '3']])
    console.log(map.entries());
    // MapIterator {1 => "1", 2 => "2", 3 => "3"}
    console.log(map.keys());
    // MapIterator {1, 2, 3}
    console.log(map.values());
    // MapIterator {"1", "2", "3"}
    
    var set = new Set([1, 2, 3]);
    console.log(set.entries());
    // SetIterator {1, 2, 3}
    console.log(set.keys());
    // SetIterator {1, 2, 3}
    console.log(set.values());
    // SetIterator {1, 2, 3}
    
    var string = '1,2,3';
    console.log([...string.matchAll(/[\d+]/g)]; // RegExpStringIterator
    // [
    // ["1", index: 0, input: "1,2,3", groups: undefined],
    // ["2", index: 2, input: "1,2,3", groups: undefined],
    // ["3", index: 4, input: "1,2,3", groups: undefined]
    // ]
```


### [iterable 과 iterator의 차이]

- **iterable?**
    - iterable한 객체는 iterator를 반환하는 [Symbol.iterator] 프로퍼티를 가진 객체이다.
        - Symbol.iterator는 주어진 객체의 iterator를 반환하는 함수를 명시한다.
    - **정리**
        1. 순회 할수 있는 데이터를 가지고 있어야한다.
        2. 전역 “well-known” symbol 인`Symbol.iterator` 을 메서드로 가지고 있어야한다. 또한 이 메서드는 #3 , #6 에 따라 구현되어야 한다.
        3. `Symbol.iterator` 메서드는 “iterator” 객체를 반환해야합니다
        4. “iterator” 객체는 반드시 next 라고 하는 메서드를 가져야합니다.
        5. next 에는 #1에 저장되어있는 데이터에 접근 할 수 있어야합니다.
        6. “iterator” 객체인 iteratorObj를 iteratorObj.next()하면 `{value:<stored data},done:false}` #1 데이터가 추출 되며 전부다 순회했을 경우 `{done:true}` 가 반환되도록 한다.
- **iterator?**
    - 이터레이터는 반복을 위해 설계된, 특별한 인터페이스를 가진 객체이다.
    - 이터레이터 객체는 next()를 가진다.
        - 연산의 결과로 객체를 반환한다. { value: value, done: boolean }

### [spread 문법]

- spread 문법을 이용하면 iterable 객체를 해체할 수 있다

---

## generator

### [generator는 쓰레드가 아니다]

- 쓰레드를 지원하는 언어는 여러 코드를 동시에 실행시킨다
- 제너레이터 코드는 제너레이터를 호출하는 코드와 같은 쓰레드에서 실행된다
- 동시에 실행되는 경우는 없고 yield구문에 의해서만 실행을 멈춘다

### [generator는 이터레이터다]

- `Symbol.iterator()`, . `next()` 두가지 메소드 구현만으로도 자기만의 이터레이터를 만들수 있다.
    - 객체가 이터러블 하기위해서는 `Symbol.iterator` 메서드를 추가해야한다
    - `next()` 메서드를 통해 하나씩 출력한다


```javascript
        const iterableObj = {
          [Symbol.iterator]() {  
            let step = 0;
            return {
              next() {
                step++;
                if (step === 1) {
                  return { value: 'This', done: false};
                } else if (step === 2) {
                  return { value: 'is', done: false};
                } else if (step === 3) {
                  return { value: 'iterable.', done: false};
                }
                return { value: '', done: true };
              }
            }
          },
        }
        for (const val of iterableObj) {
          console.log(val);
        }
        
        // 출력
        // This
        // is 
        // iterable.
```


- generator를 이용해서 만든 이터레이터

    `Symbol.iterator()` , `next()`가 이미 구현 되어있다


```javascript
        function * iterableObj() {
          yield 'This';
          yield 'is';
          yield 'iterable.'
        }
        for (const val of iterableObj()) {
          console.log(val);
        }
        
        // 모든 generator 는 symbol.iterator와 .next()를 기본 내장 하고있다
        // 호출 될때마다 다음 yield 까지 구동
        // this가 바뀌고 next() 호출
        
        // 동일하게 출력

```



### [어디에 generator 함수를 써야하는가]

- **Promise, async/ await**
    - Promise 자체로도 비동기 프로그래밍을 다루는데 문제 없지만 generator를 이용하면 코드의 가독성과 관리 측면에서 좋다


```javascript
        function * generator-promise() {
        let a=yield Promise1();
        console.log(a);
        
        let b=yield Promise1();
        console.log(b);
        
        let c=yield Promise1();
        console.log(c);
        }
        
        // generator-promise함수는 매번 실행하면서 yield를 만나 정지합니다.
        // 제네레이터 함수는 실행하면 Promise1 결괏값을 yield(넘겨주고)하고 함수를 종료합니다.
        // 다시 (제네레이터)함수가 실행하면 이 값을 변수 a에 저장됩니다.
        // 이런 과정을 통해 Promise 값이 변수 a, b, c에 저장되고 console.log를 통해 확인 가능합니다.
        // {value : 결과값, done: boolean값 }
```


- **Infinite Data Generator**
    - generator는 기본적으로 값을 생성한다. 조건을 무한으로 하면 무한 데이터 생성도 가능하다


```javascript
        function * randomize() {
          while (true) {
        let random = Math.floor(Math.random()*1000);
            yield random;
          }}
        
        var random= randomize();
        while(true) 
        console.log(random.next().value)

        function * naturalNumbers() {
          let num = 1;
          while (true) {
            yield num;
            num = num + 1
          }
        }
        const numbers = naturalNumbers();
        
        console.log(numbers.next().value)
        console.log(numbers.next().value)
        // 1
        // 2
```


- **느긋한 계산이 가능하다**


---

## **async/await**


- 동기코드와 유사한 스타일의 코딩스타일을 가능하게 하는게 장점, 최상위의 promise에 대해서 사용 하게된다. await로 선언시킨 함수 완료 후에 연결된 다른 함수가 실행 될 수 있게해준다.

### [어떻게 구현되었는가]


```javascript
    // ES7
    async function foo() {
      await bar();
    }
    
    // ES5 complied
    let foo = (() => {
      var _ref = _asyncToGenerator(function*() {
        yield bar();
      });
    
      return function foo() {
        return _ref.apply(this, arguments);
      };
    })();
    
    function _asyncToGenerator(fn) {
    	// generator 함수를 받아온다
      return function() {
        var gen = fn.apply(this, arguments);
    		// .apply로 generator 실행
    		// iterator 객체를 gen 안에 넣어서 클로저로 저장해둔다
    
        return new Promise(function(resolve, reject) {
          function step(key, arg) {
            try {
              var info = gen[key](arg);
    						// next() 호출, iterator 객체 소환
              var value = info.value;
            } catch (error) {
              reject(error);
              return;
            }
            if (info.done) { // true 면 종료
              resolve(value);
            } else {
              return Promise.resolve(value).then(
                function(value) {
                  step("next", value);
    							// 재귀함수를 통해 반복실행
                },
                function(err) {
                  step("throw", err);
                }
              );
            }
          }
          return step("next");
        });
      };
    }
```


- async 키워드를 generator 로 바꾸고 await 키워드는 yield 로 바꿨다

    → 비동기 로직이 완료 될때마다 적절하게 `next()` 를 호출하면 된다.

    → `bar()` 함수의 종료시점은 `bar()` 밖에 모르고 `next()`를 `bar()` 내에서 실행하게 되면 의존성이 생긴다

    → Promise와 재귀함수를 이용하여 `next()`를 대신 호출하는 함수를 만들어준다 (`_asyncToGenerator` 함수)

---


### 참고글

[Javascript와 Iterator](https://medium.com/@pks2974/javascript%EC%99%80-iterator-cdee90b11c0f)

[[Javascript] Iterator, Iterable 에 대해서](https://medium.com/@hyunwoojo/javascript-iterator-iterable-%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-2c6a7bb42d87)

[[Generator 함수를 이해해보자 (이론편)] 1. "Why" 제네레이터 함수를 써야 하는가?](https://velog.io/@rohkorea86/Generator-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%9D%B4%ED%95%B4%ED%95%B4%EB%B3%B4%EC%9E%90-%EC%9D%B4%EB%A1%A0%ED%8E%B8-%EC%99%9C-%EC%A0%9C%EB%84%A4%EB%A0%88%EC%9D%B4%ED%84%B0-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%8D%A8%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80)

[Async-await는 어떻게 구현하는가](https://medium.com/@la.place/async-await%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94%EA%B0%80-fa08a3157647)