비동기 버전의 이터레이터, 이터러블, 제너레이터
자바스크립트에서 반복을 배우다보면 만나게 되는 개념들이 있다.
ES2018 부터 이터레이터, 이터러블, 제너레이터의 비동기 버전이 있다.
- 비동기 이터레이터는
{done, value}
형태의 객체를 반환하는 것은 같은데,value
에Promise
객체가 들어있다. asyncIterator.next()
는{value: Promise, done: false}
와 같은 형태의 객체를 반환한다.- 그런데
asyncIterator
가 비동기로 호출되어야 하기 때문에, 이용 시asyncIterator.next().then(v => ...)
와 같은 형태가 되도록 설계되어 있다.
- 그런데
이터레이터의 비동기 버전 만들어보기
function fetchInSeries([...urls]) {
const asyncIteratorPrototype = Object.getPrototypeOf(
Object.getPrototypeOf(async function* () {}).prototype
);
let index = 0;
return Object.assign(Object.create(asyncIteratorPrototype), {
async next() {
if (index >= urls.length) {
return { done: true };
}
const url = urls[index++];
const response = await fetch(url);
if (!response.ok) {
throw new Error("URL 가져오기 실패: " + url);
}
return { value: await response.json(), done: false };
},
});
}
const it = fetchInSeries([
"/correct-json",
"/json-100",
"/json-200",
"/json-300",
]);
let result;
while (!(result = await it.next()).done) {
console.log(result.value);
}
async
버전의 객체 메서드를 활용해 비동기 이터레이터를 만들었다.
제너레이터의 비동기 버전 만들어보기
// 이전과 같은 역할을 하는 함수
async function* fetchInSeries([...urls]) {
for (const url of urls) {
const response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP 에러 " + response.status);
}
yield response.json();
}
}
- 이 제너레이터 비동기 버전은 위의 이터레이터 비동기 버전과 같은 기능을 한다.
- 제너레이터 함수여도
async
함수여서yield
의 결과물은Promise
가 된다.- 그렇기 때문에
async
함수의return
과 마찬가지로await
을 추가적으로 작성할 필요가 없다.
- 그렇기 때문에
for-await
구문 이용해보기
async function* fetchInSeries([...urls]) {
let skipNext = false;
for (const url of urls) {
if (skipNext) {
skipNext = false;
} else {
const response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP 에러 " + response.status);
}
skipNext = yield response.json();
}
}
}
const gt = fetchInSeries([
"/json-100",
"/json-200",
"/json-300",
"/json-100",
"/json-200",
"/json-300",
]);
console.log(await gt.next()); // {done: false, data: 100}
console.log(await gt.next(true)); // {done: false, data: 300}
- 비동기 버전의 제너레이터의 일반적인 이용이다.
async function* fetchInSeries([...urls]) {
let skipNext = false;
for (const url of urls) {
if (skipNext) {
skipNext = false;
} else {
const response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP 에러 " + response.status);
}
skipNext = yield response.json();
}
}
}
const gt = fetchInSeries([
"/json-100",
"/json-200",
"/json-300",
"/json-100",
"/json-200",
"/json-300",
]);
for await (const value of gt) {
console.log(value); // result 객체 전체를 가져오지 않고, value 만 가져온다.
}
/*
{data: 100}
{data: 200}
{data: 300}
{data: 100}
{data: 200}
{data: 300}
*/
for await
구문을 이용해 비동기 제너레이터를 이용했다.for await
은 비동기 버전의 이터레이터를 이용할 때 편리하다.- 이터레이터의 객체 결과를 반환하는 것이 아닌
value
프로퍼티에 해당하는 값만 반환한다는 점을 주의해야 한다.
async
는 상당한 최적화가 진행되었다.
async
키워드에 대한 최적화가 매우 많이 진행되어, 현재는async
를 붙여도 성능 차이가 거의 없다.async
를 마구 붙여도 상관없다.
asyncIterator
를 위한 여러가지 메서드가 지원된다.
npm
에서 제공하는 추가적인 라이브러리를 찾아보면 지원하는 것들이 있다.
반응형
'자바스크립트 > 모던 자바스크립트' 카테고리의 다른 글
모던 자바스크립트, UTF-16 이슈 해결에 관련된 문자열 함수 (0) | 2023.03.11 |
---|---|
모던 자바스크립트, 템플릿 리터럴과 템플릿 태그 함수 (0) | 2023.03.08 |
모던 자바스크립트, async await (0) | 2023.02.28 |
모던 자바스크립트, 제너레이터 (Generator) (0) | 2023.02.28 |
모던 자바스크립트, 이터러블 (iterable) 과 이터레이터 (iterator) (0) | 2023.02.28 |