비동기 이터레이터와 for‑await‑of
비동기 이터레이터를 구현하려면, 객체에 Symbol.asyncIterator 메서드를 추가해야 합니다. 이 메서드는 일반 이터레이터와 유사하지만, 각 next() 호출에서 반환하는 값이 프라미스로 감싸져 있어야 합니다.
const asyncRange = {
from: 1,
to: 5,
[Symbol.asyncIterator]() {
let current = this.from;
const last = this.to;
return {
async next() {
// 1초 대기 (비동기 작업 시뮬레이션)
await new Promise(resolve => setTimeout(resolve, 1000));
if (current <= last) {
return { done: false, value: current++ };
} else {
return { done: true };
}
}
};
}
};
(async () => {
console.log("비동기 이터레이터 시작:");
for await (const num of asyncRange) {
console.log(num); // 1, 2, 3, 4, 5 (각 값 사이에 1초씩 지연)
}
console.log("비동기 이터레이션 완료.");
})();
- Symbol.asyncIterator를 구현하여, 이터레이터 객체를 반환합니다.
- 각 next() 호출 시 1초를 기다린 후 숫자를 반환하고, 반복이 끝나면 { done: true }를 반환합니다.
- for await ... of 반복문을 사용해, 비동기 이터러블에서 값을 순차적으로 받아 처리합니다.
비동기 제너레이터
비동기 제너레이터는 일반 제너레이터 함수 앞에 async 키워드를 붙여 정의합니다. 이를 사용하면 제너레이터 내부에서 await를 사용할 수 있으며, 자동으로 비동기 이터러블을 생성할 수 있습니다.
async function* asyncSequence(start, end) {
for (let i = start; i <= end; i++) {
// 비동기 작업(딜레이)
await new Promise(resolve => setTimeout(resolve, 1000));
yield i;
}
}
(async () => {
console.log("비동기 제너레이터 시작:");
for await (const value of asyncSequence(1, 5)) {
console.log(value); // 1, 2, 3, 4, 5 (각 값 사이에 1초 딜레이)
}
console.log("비동기 제너레이터 완료.");
})();
- async function* 구문은 함수를 비동기 제너레이터 함수로 선언합니다. 비동기 제너레이터 함수는 일반 제너레이터 함수와 유사하게 실행 흐름을 제어하고 값을 산출하지만, 비동기적인 작업을 포함할 수 있다는 점이 핵심적인 차이점입니다.
- 비동기 제너레이터 함수 내부에서는 await 키워드를 사용하여 프라미스 기반 비동기 작업의 완료를 대기할 수 있습니다.
- yield 키워드는 비동기 제너레이터 함수 내에서 비동기적인 값을 산출하는 역할을 수행합니다.
비동기 제너레이터를 활용한 예시
// 백프레셔(backpressure)를 생성하는 기능을 명확하게 나타냅니다.
async function* generateBackpressure(id, interval) {
while (true) {
await new Promise(resolve => setTimeout(resolve, interval));
yield { id, value: Math.random() * 100 };
}
}
// 여러 백프레셔 스트림을 병합하는 기능을 명확히 설명합니다.
async function* mergeBackpressureStreams(...backpressures) {
while(true) {
const result = await Promise.all(backpressures.map(backpressure => backpressure.next()))
yield result;
}
}
// 스트림의 데이터를 변환하는 기능을 명시합니다.
async function* transformStream(mergeBack, fn) {
for await (const data of mergeBack) {
yield fn(data)
}
}
// 각 배치에서 최대값을 필터링하는 기능을 설명합니다.
async function* filterMaxValue(mergeBack) {
for await (const data of mergeBack) {
let max = -Infinity;
let result = null
data.forEach(e => {
if(max < e.value) {
max = e.value;
result = e;
}
})
yield result;
}
}
async function startExample() {
const backpressure1 = generateBackpressure(1, 1000);
const backpressure2 = generateBackpressure(2, 2000);
const backpressure3 = generateBackpressure(3, 3000);
const mergedStream = mergeBackpressureStreams(backpressure1, backpressure2, backpressure3)
// 구조분해를 통해서 스트림 데이터를 변환
const transformedStream = transformStream(mergedStream, data => {
return data.map(({value:{id, value}, ...rest}) => ({id, value: Math.ceil(value), ...rest}))
})
const filteredStream = filterMaxValue(transformedStream);
for await (const data of filteredStream) {
console.log(data)
}
}
startExample();
'JavaScript' 카테고리의 다른 글
[JavaScript] 모듈 내보내기 및 가져오기 (0) | 2025.02.19 |
---|---|
[JavaScript] 모듈 시스템 (0) | 2025.02.19 |
[JavaScript] 제너레이터 함수 (0) | 2025.02.18 |
[JavaScript] async/await 문법 (0) | 2025.02.18 |
[JavaScript] 마이크로태스트 큐 (0) | 2025.02.18 |