자바스크립트의 비밀스러운 삶: Async Generator
Source: Dev.to
for await...of 로 데이터 스트림을 처리하는 방법.
Timothy는 관자놀이를 문질렀다. 화면에 보인 함수는 마치 패배를 눈치챈 전투처럼 보였다.
async function getAllUsers() {
let url = '/api/users?page=1';
const allUsers = [];
while (url) {
const response = await fetch(url);
const data = await response.json();
// Add this page's users to our big list
allUsers.push(...data.users);
// Prepare for the next loop... if there is one
url = data.nextPage;
}
return allUsers;
}
“모든 사용자 데이터를 다운로드하려고 하는데,” Timothy가 Margaret에게 설명했다. “하지만 사용자가 5만 명이나 돼. 모든 페이지를 다 받아야만 처리하기 시작하면 사용자는 20초 동안 기다려야 해. 마치… 멈춰버린 느낌이야.”
Margaret는 고개를 끄덕였다. “당신은 스트림을 버킷처럼 다루고 있어,” 라고 그녀가 말했다.
“모든 물방울을 모아야만 마시게 하려는 거야,” 라고 이어 말했다. “그럼 호스에서 바로 마시게 하면 안 될까?”
The Hybrid
Margaret는 보드에 새로운 구문을 적었다. 그것은 언어에서 가장 강력한 두 키워드를 결합한 것이었다.
async function* fetchUsers() { ... }
Async와 Generator가 만나었다. async는 네트워크를 기다릴 수 있게 해 주고, *는 데이터를 한 조각씩 yield 할 수 있게 해 준다.
그녀는 Timothy의 코드를 다시 작성하면서 안전망을 추가했다.
async function* fetchUsers() {
let url = '/api/users?page=1';
while (url) {
try {
const response = await fetch(url);
const data = await response.json();
// Instead of building a massive array, we deliver this page immediately
for (const user of data.users) {
yield user;
}
url = data.nextPage;
} catch (error) {
console.error("Stream interrupted", error);
return; // Stop the stream safely
}
}
}
The Magic Loop (for await...of)
“여기가 마법이 일어나는 부분이야,” Margaret가 말했다. “우리는 기다릴 줄 아는 루프가 필요해.”
그녀는 소비자 코드를 적었다:
const userStream = fetchUsers();
for await (const user of userStream) {
console.log("Processing:", user.name);
// This loop automatically PAUSES while the next page downloads!
}
Timothy는 콘솔 시뮬레이션을 지켜보았다.
- 루프가 10명의 사용자를 즉시 출력한다.
- 루프가 일시 정지한다 (네트워크가 페이지 2를 가져오는 동안).
- 루프가 다시 깨어나서 또 10명을 출력한다.
“일시 정지는 눈에 보이지 않아,” Timothy가 속삭였다.
“맞아,” Margaret가 대답했다. “루프 안의 코드는 기다리고 있다는 걸 전혀 알지 못해. 단지 다음 사용자를 요구하고, JavaScript가 일시 정지를 처리해 주는 거야. 당신은 메모리 스냅샷을 처리하는 것이 아니라 시간을 처리하고 있는 거지.”
The Emergency Brake
“마지막으로 한 가지 더,” Margaret가 속삭이듯 말했다. “실제 환경에서는 스트림이 무한히 이어질 수도 있어. 사용자가 페이지를 떠날 때도 있지.”
“그럼 어떻게 해야 하지?”
“AbortController를 사용하면 돼,” 라고 그녀가 답했다. “호스를 끊을 수 있게 해 주는 거야. 스트림은 언제든 중단될 수 있도록 설계하는 것이 좋다.”
The Conclusion
Timothy는 allUsers 배열을 삭제했다. 이제는 더 이상 버킷이 필요 없었다.
“가벼워진 느낌이야,” Timothy가 말했다. “데이터를 쌓아두지 않아도 돼.”
“그게 바로 Async Generator의 선(禪)이야,” Margaret가 미소 지으며 말했다. “미래의 무게를 짊어지지 마. 지금 눈앞에 있는 것만 다루면 돼.”