Iteration

JS의 반복을 공부하다보면 이터러블, 이터레이터와 같은 단어를 마주하게 되는데 볼때마다 헷갈린다.
본인의 착각일 수도 있지만 사람들마다 뭔가 다른 의미로 사용하는 듯해서 더욱 헷갈렸다.
그래서 공식 명세에 적힌 내용을 정리해서 정의를 명확하게 해보자.

Iteration이란?

Iteration은 뜻 그대로 반복이라는 의미를 가진다.
JS에서 순회를 통해 각 요소에 접근하기 위해선 iteration 인터페이스에 속해 있는 인터페이스를 구현해야 한다.

iteration 인터페이스에는 아래와 같은 인터페이스가 속해 있다.

  • iterable 인터페이스
  • iterator 인터페이스
  • iteratorResult 인터페이스
  • asyncIterable 인터페이스
  • asyncIterator 인터페이스

Iterable 인터페이스

iterable 인터페이스는 @@iterator를 프로퍼티 키로 사용하는 메서드를 포함한 객체를 생성하면 된다.
이미 정의된 Symbol인 Symbol.iterator를 키로 사용하면 된다.
Symbol.iterator를 키로 사용한 메서드는 iterator 인터페이스를 준수한 iterator 객체를 반환해야 한다.

const iterable = { // iterable 인터페이스
	[Symbol.iterator]() { ... }
}

Iterator 인터페이스

iterator 인터페이스에는 next를 프로퍼티 키로 사용하는 메서드를 반드시 포함한 객체를 생성해야 한다.
next메서드는 iteratorResult 인터페이스를 준수하는 iteratorResult 객체를 반환해야 한다.

const iterable = {
	[Symbol.iterator]() {
		...
		return { // iterator 인터페이스
			next() { ... }
		}
	}
}

필수는 아니지만, 앞선 next메서드의 반환값이 done 값이 true인 iteratorResult 객체인 경우, 이후 호출되는 next메서드의 반환값으로 done 값이 true인 iteratorResult 객체를 반환하도록 구현하면 된다.

선택사항으로 returnthrow를 프로퍼티 키로 사용하는메서드를 포함해도 된다.
두 메서드도 iteratorResult 인터페이스를 준수하는 iteratorResult 객체를 반환해야 한다.

return메서드는 선택적으로 인수에 값을 받아 iteratorResult 객체의 value 프로퍼티에 할당하고,
done 프로퍼티에는 true를 할당해서 iteratorResult 객체를 반환하면 된다.

return메서드를 호출하면 더 이상 next메서드를 호출하지 않는다는 의미를 가진다.

return(value) {
	return { value, done: true };
}

throw메서드는 선택적으로 인수에 값을 받아 done 프로퍼티에는 true를 할당해서 iteratorResult 객체를 반환하면 된다.
인수에는 일반적으로 Error 인스턴스를 할당한다.

throw(error) {
	return { done: true };
}

IteratorResult 인터페이스

iteratorResult 인터페이스에는 불리언 값을 가지는 done 프로퍼티와 ECMAScript language 값을 가지는 value 프로퍼티를 포함한 객체를 생성하면 된다.
ECMAScript language 값에는 Undefined, Null, Boolean, String, Symbol, Number, BigInt, 그리고 Object가 속해 있다.

const iterable = {
  [Symbol.iterator]() {
    let i = 0;
    return {
      next() {
        if (i >= 10) {
          return { done: true };
        }

        return { value: i++, done: false }; // iteratorResult 인터페이스
      }
    };
  }
};

AsyncIterable 인터페이스

asyncIterable 인터페이스는 @@asyncIterator를 프로퍼티 키로 사용하는 메서드를 포함한 객체를 생성하면 된다.
이 메서드는 asyncIterator 인터페이스를 준수한 asyncIterator 객체를 반환해야 한다.
asyncIterator 인터페이스를 준수한 iterable 객체의 반복 작업은 for await (let item of iterable) 반복문을 사용해 처리해야 한다.

const iterable = { // asyncIterable 인터페이스
	[Symbol.asyncIterator]() { ... }
}

AsyncIterator 인터페이스

asyncIterator 인터페이스는 next를 프로퍼티 키로 사용하는 메서드를 포함한 객체를 생성해야 한다.
이 메서드는 iteratorResult 인터페이스를 준수하는 iteratorResult 객체를 프로미스로 감싸서 반환해야 한다.

const iterable = {
	[Symbol.asyncIterator]() {
		return { // asyncIterator 인터페이스
			async next() { ... }
		}
	}
}

필수는 아니지만, 앞선 next메서드의 반환값이 done 값이 true인 iteratorResult 객체를 감싼 프로미스인 경우, 이후 호출되는 next메서드의 반환값으로 done 값이 true인 iteratorResult 객체를 감싼 프로미스를 반환하도록 구현하면 된다.
선택사항으로 returnthrow를 프로퍼티 키로 사용하는 메서드를 포함해도 된다.
두 메서드도 iteratorResult 인터페이스를 준수하는 프로미스로 감싼 iteratorResult 객체를 반환해야 한다.


정리하면서 느낀 점은 헷갈리는 단어는 공식 명세를 보는 게 도움이 된다.