Async generator (async function*) + for await 는 pull 기반 자연스러운 backpressure 제공. consumer 가 await 끝낼 때까지 producer 가 다음 yield 안 함. RxJS 같은 push 모델보다 단순.
📖 핵심 개념
async function* gen() → 호출하면 AsyncGenerator.
for await (const x of gen()) 로 소비.
yield 시점에 consumer 가 await 중이면 producer suspend.
자연스러운 cleanup: try/finally 가 break/throw 시에도 실행.
💻 코드 패턴
페이지 fetch 스트리밍
asyncfunction*paginate<T>(url: string):AsyncGenerator<T>{letcursor: string|null=null;do{constres=awaitfetch(`${url}?cursor=${cursor??''}`).then(r=>r.json());for(constitemofres.items)yielditem;cursor=res.nextCursor;}while(cursor);}// 소비자가 일찍 멈출 수 있음
forawait(constuserofpaginate<User>('/api/users')){if(awaitshouldStop(user))break;// generator 의 finally 자동 실행
awaitprocess(user);}
asyncfunction*watch():AsyncGenerator<Event>{constctrl=newAbortController();try{forawait(constevofsubscribe(ctrl.signal))yieldev;}finally{ctrl.abort();// break / throw 시 자동
}}
🤔 의사결정 기준
데이터
권장
한 번에 다 메모리에 못 담음
async iterator
페이지네이션 / 무한 스크롤 source
async iterator
한 번 producer 시작 후 다수 consumer
async iterator X — EventEmitter / SharedFlow
HTTP streaming response
ReadableStream → async iterator (for await (const chunk of res.body))
RxJS 가 더 자연스러운 (combine, debounce, retry)
RxJS
❌ 안티패턴
소비 중 break 후 cleanup 안 함: try/finally 누락 → connection / file leak.
Promise.all 로 모두 모은 후 yield: pull 의 의미 없음. 메모리 폭발.
await 없는 yield: producer 가 즉시 다음 yield → backpressure 의미 잃음.
async iterator 안에서 setState 직접: React 컴포넌트면 unmount 후 위험. AbortSignal 결합.
iterator 이중 소비: 대부분 single-use. tee / branching 라이브러리 필요.
for-of 로 async iterator 소비: 그냥 Promise[] 반환 — 안 동작. for-await 필수.
🤖 LLM 활용 힌트
페이지네이션 / 라인 처리 / streaming HTTP 는 디폴트 async generator.