Iterator
- 배열이나 유사한 자료구조 내부를 순회하는 객체이다
- iterable, iterator 두가지 iterable protocol이 존재
[Iterable]
- 객체 멤버를 반복할 수 있는 객체
- 자바스크립트에서 객체가 iterable 하기 위해서는
[@@iterator]
메소드 구현 필요, object property에Symbol.iterator
를 추가해야 한다.
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
// 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를 반환하는 함수를 명시한다.
-
정리
- 순회 할수 있는 데이터를 가지고 있어야한다.
- 전역 “well-known” symbol 인
Symbol.iterator
을 메서드로 가지고 있어야한다. 또한 이 메서드는 #3 , #6 에 따라 구현되어야 한다. -
Symbol.iterator
메서드는 “iterator” 객체를 반환해야합니다 - “iterator” 객체는 반드시 next 라고 하는 메서드를 가져야합니다.
- next 에는 #1에 저장되어있는 데이터에 접근 할 수 있어야합니다.
- “iterator” 객체인 iteratorObj를 iteratorObj.next()하면
{value:<stored data},done:false}
#1 데이터가 추출 되며 전부다 순회했을 경우{done:true}
가 반환되도록 한다.
- iterable한 객체는 iterator를 반환하는 [Symbol.iterator] 프로퍼티를 가진 객체이다.
-
iterator?
- 이터레이터는 반복을 위해 설계된, 특별한 인터페이스를 가진 객체이다.
- 이터레이터 객체는 next()를 가진다.
- 연산의 결과로 객체를 반환한다. { value: value, done: boolean }
[spread 문법]
- spread 문법을 이용하면 iterable 객체를 해체할 수 있다
generator
[generator는 쓰레드가 아니다]
- 쓰레드를 지원하는 언어는 여러 코드를 동시에 실행시킨다
- 제너레이터 코드는 제너레이터를 호출하는 코드와 같은 쓰레드에서 실행된다
- 동시에 실행되는 경우는 없고 yield구문에 의해서만 실행을 멈춘다
[generator는 이터레이터다]
-
Symbol.iterator()
, .next()
두가지 메소드 구현만으로도 자기만의 이터레이터를 만들수 있다.- 객체가 이터러블 하기위해서는
Symbol.iterator
메서드를 추가해야한다 -
next()
메서드를 통해 하나씩 출력한다
- 객체가 이터러블 하기위해서는
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()
가 이미 구현 되어있다
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를 이용하면 코드의 가독성과 관리 측면에서 좋다
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는 기본적으로 값을 생성한다. 조건을 무한으로 하면 무한 데이터 생성도 가능하다
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로 선언시킨 함수 완료 후에 연결된 다른 함수가 실행 될 수 있게해준다.
[어떻게 구현되었는가]
// 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, Iterable 에 대해서
Top comments (0)