dicem
@dicem

Как правильно сделать асинхронный генератор с неявным кол-вом выполнения?

Всем привет, имею следующий код:

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
}


В этом примере g1 получает первые 100 штук записей как мне и нужно, а в g2 уже done: true и value: undefined

Кто нибудь сможет понять мне, в чем я не прав или же нужен более конкретный пример с запросами?
  • Вопрос задан
  • 64 просмотра
Пригласить эксперта
Ответы на вопрос 2
IvanU7n
@IvanU7n
655e276f40b55584651053.png
_page и _pages из
console.log(`Обработано страниц листинга ${_page - 1} / ${_pages}`)
не существуют
Ответ написан
Комментировать
@mvv-rus
Настоящий админ AD и ненастоящий программист
Я долго думал над предыдущем ответом и понял почему вы его не пометили - при том, что он таки является ответом (и это очевидно: в нем кристалльно прозрачно (если конечно как следует подумать) сказано, что вы пытаетесь использовать неинициализованные переменные в операции деления внутри опреатора 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 внутрь цикла. Кстати запомните этот прием: он обязательно пригодится вам при дальнейшей разработке костылей (правда следите при этом, чтобы цикл все-таки закончился, а то конфуз будет).
Если же вы почему-то решили исправить ошибку, используйте, руководствуясь намеками из предыдущего ответа, правильные имена переменных.

И обязательно пометьте предыдущий ответ: его автор нашел ошибку, о которой он аккуратно вам намекнул, раньше чем я даже просто прочитал ваш вопрос.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы