Front-end/JavaScript
[JavaScript] async 이터레이터와 제너레이터
Nave
2022. 5. 31. 13:15
비동기 이터레이터를 사용하면 비동기적으로 들어오는 데이터를 필요에 따라 처리할 수 있다. 네트워크를 통해 데이터가 여러 번에 걸쳐 들어오는 상황을 처리할 수 있기 때문.
더보기
1. async 이터레이터
비동기 이터레이터는 일반 이터레이터와 유사하며, 약간의 문법적인 차이가 있다.
let range = {
from: 1,
to: 5,
// for..of 최초 실행 시, Symbol.iterator을 호출해야함
[Symbol.iterator]() {
// Symbol.iterator메서드는 이터레이터 객체를 반환
// 이후 for..of는 반환된 이터레이터 객체만을 대상으로 동작하는데,
// 다음 값은 next()에서 정해진다.
return {
current: this.from,
last: this.to,
// for..of 반복문에 의해 각 이터레이션마다 next()가 호출됨
next() { // (2)
// next()는 객체 형태의 값, {done:.., value :...}을 반환
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
}
};
for(let value of range) {
alert(value); // 1, 2, 3, 4, 5
}
-> 1부터 5까지 연속된 수를 반환하는 이터레이터
이 이터러블 객체를 비동기적으로 만드려면
- Symbol.iterator 대신 Symbol.asyncIterator를 사용해야한다
- next()는 프라미스를 반환해야 한다.
- 비동기 이터러블 객체를 대상으로 하는 반복 작업은 for await (let item of iterable) 반복문을 사용해 처리해야 한다.
//위의 예시를 토대로 일초마다 비동기적으로 값을 반환하는 이터러블 객체
let range = {
from: 1,
to: 5,
// for await..of 최초 실행 시, Symbol.asyncIterator를 호출해야함
[Symbol.asyncIterator]() { // (1)
// Symbol.asyncIterator 메서드는 이터레이터 객체를 반환
// 이후 for await..of는 반환된 이터레이터 객체만을 대상으로 동작하는데,
// 다음 값은 next()에서 정해집니다.
return {
current: this.from,
last: this.to,
// for await..of 반복문에 의해 각 이터레이션마다 next()가 호출된다
async next() { // (2)
// next()는 객체 형태의 값, {done:.., value :...}를 반환
// (객체는 async에 의해 자동으로 프라미스로 감싸진다.)
// 비동기로 무언가를 하기 위해 await를 사용할 수 있다.
await new Promise(resolve => setTimeout(resolve, 1000)); // (3)
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
}
};
(async () => {
for await (let value of range) { // (4)
alert(value); // 1,2,3,4,5
}
})()
--> async 이터레이터와 일반 이터레이터의 차이점
- 객체를 비동기적으로 반복 가능하도록 하려면, Symbol.asyncIterator메서드가 구현되어 있어야한다.
- Symbol.asyncIterator는 프라미스를 반환하는 메서드인 next()가 구현된 객체를 반환해야한다.
- next()는 async 메서드일 필요는 없는데, 프라미스를 반환하는 메서드라면 일반 메서드도 괜찮다. 근데 여기서는 async를 사용하면 await도 사용할 수 있기 때문에, 편의상 async메서드를 사용해 일 초의 딜레이가 생기도록 했음
- 반복작업을 하려면 ‘for’ 뒤에 'await’를 붙인 for await(let value of range)를 사용하면 된다.
2. async 제너레이터
//start부터 end까지의 연속된 숫자를 생성해주는 제너레이터
function* generateSequence(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
for(let value of generateSequence(1, 5)) {
alert(value); // 1, then 2, then 3, then 4, then 5
}
이 제러레이터를 async 제너레이터로 변형하면
async function* generateSequence(start, end) {
for (let i = start; i <= end; i++) {
await new Promise(resolve => setTimeout(resolve, 1000));
yield i;
}
}
(async () => {
let generator = generateSequence(1, 5);
for await (let value of generator) {
alert(value); // 1, 2, 3, 4, 5
}
})();
--> async 키워드를 붙이기만 하면 제너레이터 안에서 프라미스와 기타 async 함수를 기반으로 동작하는 await를 사용할 수 있다.
async 제너레이터의 generator.next() 메서드는 비동기적이 되고, 프라미스를 반환한다는 점은 일반 제너레이터와 async 제너레이터엔 또 다른 차이이다.
더보기
3. async 이터러블
반복 가능한 객체를 만드려면 객체에 Symbol.iterator을 추가해야 한다.
let range = {
from: 1,
to: 5,
[Symbol.iterator]() {
return <range를 반복가능하게 만드는 next가 구현된 객체>
}
}
//요런 식으로
그런데 Symbol.iterator는 위 예시와 같이 next가 구현된 일반 객체를 반환하는 것 보다, 제너레이터를 반환하도록 구현하는 경우가 더 많다.
let range = {
from: 1,
to: 5,
*[Symbol.iterator]() { // [Symbol.iterator]: function*()를 짧게 줄임
for(let value = this.from; value <= this.to; value++) {
yield value;
}
}
};
for(let value of range) {
alert(value); // 1, 2, 3, 4, 5
}