Как правильно сделать асинхронный генератор с неявным кол-вом выполнения?
Всем привет, имею следующий код:
async function* categoriesGenerator (categoryId) { let page = 1 let pages = 1 try { while (page <= pages) { /** * Задержка перед началом операции */ await delay(1000) // Тут функция которая просто генерит FormData для запроса const formdata = getSearchFormData(page, categoryId) const res = await axios.post('blablabla', formdata, { headers: { 'Content-Type': 'multipart/form-data' } }) yield res.data.result.list console.log(`Обработано страниц листинга ${_page - 1} / ${_pages}`) page = res.data.result.page + 1 pages = res.data.result.pages } } catch (error) { console.log('Просто бесполезная ошибка чтоб понимать что происходит через vs code'овский дебагер') } } |
async function* categoriesGenerator (categoryId) { let page = 1 let pages = 1 try { while (page <= pages) { /** * Задержка перед началом операции */ await delay(1000) // Тут функция которая просто генерит FormData для запроса const formdata = getSearchFormData(page, categoryId) const res = await axios.post('blablabla', formdata, { headers: { 'Content-Type': 'multipart/form-data' } }) yield res.data.result.list console.log(`Обработано страниц листинга ${_page - 1} / ${_pages}`) page = res.data.result.page + 1 pages = res.data.result.pages } } catch (error) { console.log('Просто бесполезная ошибка чтоб понимать что происходит через vs code'овский дебагер') } }
Это результат моего гуглежа про генераторы и асинхронный цикл который с его помощью можно реализовать. Суть следующая, на сервере есть N-ое кол-во записей в категории, однако мы не можем вытащить их всех разом, а только по 100 штук и с определенной задержкой, чтоб сервер не жаловался на спам запросами.
Для этого мне нужен генератор, который бы отправлял запросы с каждой итерацией, пока не выполнится условие, что текущая страница (page) не станет больше чем общее число страниц (pages) (страницы в этом контексте это запросы на сервер по 100 штук записей).
Однако в моей функции генератор отрабатывает 1 раз и дальше уже не отрабатывает.
Запускаю этот код следующим образом
async function generateRecipyList () { const generator = categoriesGenerator(35) const list = [] const g1 = await generator.next() const g2 = await generator.next() const g3 = await generator.next() // for await (let _list of generator) { // list.push(..._list) // } // return list } |
async function generateRecipyList () { const generator = categoriesGenerator(35) const list = [] const g1 = await generator.next() const g2 = await generator.next() const g3 = await generator.next() // for await (let _list of generator) { // list.push(..._list) // } // return list }
В этом примере g1 получает первые 100 штук записей как мне и нужно, а в g2 уже done: true и value: undefined
Кто нибудь сможет понять мне, в чем я не прав или же нужен более конкретный пример с запросами?
Дополнительно:
Ответы:
_page и _pages из
console.log(`Обработано страниц листинга ${_page - 1} / ${_pages}`) |
console.log(`Обработано страниц листинга ${_page - 1} / ${_pages}`)
не существуют
Я долго думал над предыдущем ответом и понял почему вы его не пометили - при том, что он таки является ответом (и это очевидно: в нем кристалльно прозрачно (если конечно как следует подумать) сказано, что вы пытаетесь использовать неинициализованные переменные в операции деления внутри опреатора console.log, который у вас следует после yield).
А подумав, я все же решил удовлетворить свою страсть к графомансву и разъяснить вам, почему смысл предыдущего ответа ответ столь очевиден - если хорошо и долго думать, естественно. Но если вам это и так явно, или вы уже все поправили, или для вас в этом разъяснении сликом много букв, то не читайте далше, а просто отметьте предыущий.
Но раз уж вы до сюда читать дошли, продолжу. Как уже сказано в предыдущем ответе, имена этих неопределенных переменных - _page и _pages. И да, для JS это(внезапно) - уже другие переменные, нежели page и pages, которым вы что-то там присваиваете: в JS встроен слишком слабый AI, чтобы понять, что это - одно и то же, а символы подчеркивания надо игнорировать.
Это переменным page и pages (без подчеркивания), а не _page и _pages вы таки присваиваете значения, хоть и немного поздновато: данные рассчитанные из этих значений, если бы у JS хватило ума догадаться, как должно быть, были бы выведены на консоль при следующем проходе цикла - что обязательно сбило бы вас с толку, если бы вы когда-нибудь обратили бы внимание на вывод в консоли - потому что данные относились бы не к тому циклу. Но так или иначе, если бы AI JS был получше (или вы бы использовали правильные имена переменных), то код бы в целом работал столько раз, сколько надо(хоть и вывел бы на консоль нечто страннное, впрочем, это уже фигня, да?).
А так, из-за слабости AI JS, один-то раз у вас yield выполняется, потому что он все же до console.log. Но вот дальше из-за использования неинициализованных переменных будет выброшена ошибка. Да, эту ошибку ваш код успешно перехватывает и давит, но - уже вне цикла. И все - цикл завершен, и генератор с чистой совестью завершает свою работу.
Ошибка же, в рамках моего скромного понимания, там должна там быть какой-то на тему работы с NaN (значение неинециализованной переменной - undefined - при преобразовании в число дает именно NaN). Мне это проверять лень (и вообще я программист ненастоящий), но вы, если вам это было бы любопытно, могли бы даже увидеть и тип, и сообщение об ошибке, если бы вместо того, чтобы давить ошибку с выводом неинформативного текста, вывели бы свойства name и message объекта ошибки (error.name и error.message в вашем случае). Этот совет - он, конечно, на случай, что вы желаете понимать, что там за ошибка. Но, может, вам надо просто убрать ее от юзера с глаз долой, чтобы не мозолила глаза -тогда указанные свойства выводить, конечно, незачем.
Как это имеющееся положение поправить - это я вам не решусь советовать, не зная точно ваших намерений. Если вы хотите, как часто это делается в реальной разработке, замести ошибку под ковер, чтобы не оствечивала (что логично, если вам надо это заставить код работать хоть как-то) чтобы выполнить yield должное количество раз - просто перенесите оператор try/catch внутрь цикла. Кстати запомните этот прием: он обязательно пригодится вам при дальнейшей разработке костылей (правда следите при этом, чтобы цикл все-таки закончился, а то конфуз будет).
Если же вы почему-то решили исправить ошибку, используйте, руководствуясь намеками из предыдущего ответа, правильные имена переменных.
И обязательно пометьте предыдущий ответ: его автор нашел ошибку, о которой он аккуратно вам намекнул, раньше чем я даже просто прочитал ваш вопрос.
Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.
Пока нет других ответов. Будьте первым, кто поможет автору.
Ответить на вопрос

Для создания асинхронного генератора с неявным количеством выполнений вам необходимо использовать ключевое слово `yield` внутри асинхронной функции. Это позволит вам создавать итераторы, которые могут возвращать значения по мере их генерации, без блокирования основного потока выполнения.
Прежде всего, убедитесь, что ваша среда поддерживает асинхронные функции, такие как `async function` и `await`. В языке программирования JavaScript это обычно поддерживается с использованием синтаксиса ES6 и выше.
Вот пример, как можно создать асинхронный генератор с неявным количеством выполнений на языке программирования JavaScript:
async function* asyncGenerator() { let i = 0; while (true) { yield await someAsyncOperation(i++); } } async function someAsyncOperation(index) { return new Promise(resolve => { setTimeout(() => { resolve(index); }, 1000); }); } (async () => { const generator = asyncGenerator(); for await (const value of generator) { console.log(value); } })();
В этом примере `asyncGenerator` является асинхронной функцией-генератором, которая будет выполняться асинхронно и возвращать значения по мере генерации. Функция `someAsyncOperation` представляет собой асинхронную операцию, которая также выполняется асинхронно.
Затем вы можете использовать цикл `for await...of` для итерации по значениям, возвращаемым асинхронным генератором. Это позволит вам обрабатывать значения по мере их генерации, не блокируя основной поток выполнения.
Таким образом, используя асинхронные функции и ключевое слово `yield`, вы можете создать асинхронный генератор с неявным количеством выполнений, который позволит вам эффективно обрабатывать результаты асинхронных операций.