Iteration protocols

어떤 객체가 String, Array 같이 순회 (iteration) 할 수 있는 타입이 아니더라도, JavaScript에서 순회가 가능한 (iterable) 객체로 만드는 방법이 있다. 이를 Iteration protocol 이라하며 interable, iterator 를 만드는 2개의 규칙(protocol) 을 기술하고 있다.

iterable 규약에 순응하는 객체는 for...of syntax, Spread syntax, destructuring syntax 등에서 사용이 가능해진다.

1. iterable 정의

iterable로 정의하기 위해서는 아래와 같은 내용을 객체 또는 prototype chain내 객체에서 아래 내용을 구현하여야 한다.

  • [Symbol.iterator] (혹은 @@iterator) 란 Property로 메소드를 구현해야한다.
  • 해당 메서드는 iterator 객체를 반환하여야 한다.

2. iterator 정의

iterable 에서 [Symbol.iterator] 호출에 의해 반환되는 iterator 순회할 때 마다 결과를 반환한다. 결과를 반환하기 위해 iterator은 아래의 프로퍼티를 구현한다.

    1. next 란 이름의 메소드 구현 (필수)
    • next 메소드는 1개 혹은 0개의 파라미터를 전달 받을 수 있으며, IteratorResult 인터페이스를 준수하는 객체를 반환한다.
      • 반환값이 undefined의 경우 { done: false, value: undefined} 와 동일
    1. return(value), throw(exception) 메소드 구현 (옵션)
    • return() : IteratorResult 인터페이스를 준수하는 객체를 반환한다. 이후 next 메소드는 더 호출되지 않는다.
    • throw() IteratorResult 인터페이스를 준수하는 객체를 반환한다. 필요한 경우 이 메소드르 호출하여 iterator에게 오류가 감지되었음을 알린다.

2.1 IteratorResult Interface

IteratorResult Interface

  • done : true/false로 반복이 끝났는지를 나타낸다. 끝났으면 true로 설정
  • value : 현재 반환되는 값을 설정

예시

iterable 구현 일반

1
2
3
4
5
6
7
8
9
10
11
let i = 0;
const iterable = {
[Symbol.iterator]() {
return this;
}, // i
next() {
return i > 3 ? { done: true } : { value: i++, done: false };
},
};

for (let n of iterable) console.log(n); // 0, 1, 2, 3

iterable 의 return() 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const customIterator = {
currentIndex: 0,

[Symbol.iterator]() {
return this;
},

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

return() {
console.log('Closing the iterator.');
// 여기서 리소스 정리 또는 마무리 작업을 수행
return { done: true };
},
};

for (const num of customIterator) {
console.log(num);
if (num === 3) {
break; // 이터레이션을 중단. 이때 return() 메서드가 호출되고 이터레이션을 종료
}
}

Node.js 의 /lib/events.js 에서 구현 (실 활용 예제)

iterator의 return() 함수를 정의하여 리소스를 해제하는데 사용한다.

events.jslink
1103
1104
1105
return() {
return closeHandler();
},

iteration 도중 오류를 핸들링하기 위해 throw(err) 함수를 정의한다.

events.jslink
1107
1108
1109
1110
1111
1112
1113
throw(err) {
if (!err || !(err instanceof Error)) {
throw new ERR_INVALID_ARG_TYPE('EventEmitter.AsyncIterator',
'Error', err);
}
errorHandler(err);
},

iteration을 위한 next() 호출이 될때 IteratorResult 형식으로 현재 값을 반환한다.

events.jslink
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
const iterator = ObjectSetPrototypeOf({
next() {
// First, we consume all unread events
if (size) {
const value = unconsumedEvents.shift();
size--;
if (paused && size < lowWatermark) {
emitter.resume();
paused = false;
}
return PromiseResolve(createIterResult(value, false));
}

next() 호출에 대해 undefined를 반환함으로써 iteration이 종료되었음을 알린다.

events.jslink
1094
1095
1096
// If the iterator is finished, resolve to done
if (finished) return closeHandler();

출처

Share