Для чего используют AsyncIterator в JavaScript
В MDN есть статья про AsyncIterator, однако, технология кажется весьма узкоспециализированной. Не понятен кейс использования: задачи, которые она решает, значительно более читаемым методом будет реализовать через Promise.all или с помощью цикла с оператором await в теле
Хотелось бы получить пример коммерческого использования, где без данной синтаксической конструкции обойтись не удастся
Ответы (1 шт):
Используя AsyncIterator можно эффективно обойти базу данных, не выгружая ее в оперативную память. Например, используя Promise.all нам потребуется хранить единовременно в оперативной памяти массив всех id-шников из таблиц, может не хватить места
const listDocuments = async function* (databaseId, collectionId, queries = []) {
let lastId = null;
const createRequest = async () => {
console.log(`Fetching collection ${collectionId} from ${collectionId} starting from ${lastId || 0}`)
return await databases.listDocuments(
databaseId,
collectionId,
[
sdk.Query.orderDesc("$updatedAt"),
...(lastId ? [sdk.Query.cursorAfter(lastId)] : []),
...queries,
sdk.Query.limit(DOCUMENTS_PAGE_SIZE),
]
);
};
let counter = 0;
let lastQuery = createRequest();
while (counter < TOTAL_DOCUMENTS_LIMIT) {
const [{ documents }] = await Promise.all([
lastQuery,
sleep(DOCUMENT_READ_DELAY),
]);
for (const document of documents) {
yield document;
}
if (documents.length < DOCUMENTS_PAGE_SIZE) {
break;
}
lastId = documents[documents.length - 1].$id;
lastQuery = createRequest();
counter += documents.length;
}
};
Данный код позволяет считывать данные из базы без индексов циклом с предусловием, однако, запрос выполняется в фоне вместе с фильтрами строк в синхронном виде. Помимо этого, можно установить задержку так, чтобы один запрос выполнялся не менее чем раз в секунду, если есть ограничение на балансировщике. Примечательно, что задержка высчитывается на момент начала запроса, время исполнения запроса не учитывается
const createPaginate = (limit, offset) => {
const result = [];
return (rows = []) => {
for (const row of rows) {
if (offset > 0) {
offset -= 1;
continue;
}
if (limit > 0) {
result.push(row);
limit -= 1;
continue;
}
break;
}
return {
rows: result,
done: limit <= 0,
};
}
};
...
const paginate = createPaginate(25, 0)
...
for await (let rows of repository.listDocuments(request)) {
rows = rows.filter(({ price }) => price > filterData.priceFrom)
rows = rows.filter(({ price }) => price < filterData.priceTo)
...
if (paginate(rows).done) {
break;
}
}
const { rows } = paginate();
response.json(rows);
Ограничения по limit и offset позволяют сборщику мусора планово выгружать не используемые данные. Код академического примера взят из этого репозитория. Крайне полезно, если нужно реализовать фильтр, написать который SQL запросом технически сложно
