Рекурсивный доход вызывается из setTimeout

Я использую API с ограничением скорости, каждый раз, когда я нажимаю на ограничение скорости, он возвращает заголовок retry-after, указывающий количество секунд ожидания сброса ограничения скорости.

Мне необходимо:

  • Отправьте 100 звонков с помощью Promise.allSettled([...]);
  • Некоторые запросы будут успешными, а затем обработают их;
  • Повторить отклоненные запросы через указанные секунды.

Мое решение до сих пор:

async *indicators(items: string[]): AsyncIterableIterator<any[]> {
  const res = await Promise.allSettled(items.map((item) => this.makeRequest(item)))

  const fulfilledRequests = res.filter((r) => r.status === 'fulfilled') as PromiseFulfilledResult<any>[]

  for (const { value } of fulfilledRequests) {
    console.info('Yielding')
    yield value
    console.info('Yielded')
  }

  const rejectedRequest = res.find((r) => r.status === 'rejected') as any
  const failedItems = res.filter((p) => p.status === 'rejected').map(({ reason }: any) => reason.item)

  if (failedItems.length === 0 || !failedItems?.reason?.retryAfter)
    return Logger.log(`No more items to check`)

  setTimeout(this.indicators(failedItems).next.bind(this), rejectedRequest.reason.retryAfter)
}

async makeRequest(item: string): Promise<Indicator[]> {
  try {
    const { data: { data } } = await firstValueFrom(this.httpService.post('https://api.io', { item }))
    return data
  } catch (error) {
    throw { retryAfter: error.response.headers['retry-after'] * 1000, symbol }
  }
}

main() {

  for await (const item of this.indicators(['', ''])) {
    console.info(item)
  }

}
  • Первые итерации выполняются нормально, из 100 элементов он извлекает 30 и дает ожидаемый результат;
  • Затем setTimeout работает как положено;
  • Функции индикаторов запускаются во второй раз;
  • Запрос работает;
  • Показывается первый журнал Yielding, а затем он останавливается.

Я использую NestJS с Typescript на Node v16.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
31
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я бы предложил повторять каждый запрос независимо до тех пор, пока он не будет успешным или отказом.

Ниже приведен код НЕ ИСПЫТАНО.


async function main() {
  const promises = ["", ""].map(item=>makeRequestUntilDone(item));
  const results = await Promise.allSettled(promises);
  for(const r of results) {
      if (r.status === 'fulfilled') {
        // process result
        console.info(r.value);
      } else {
        // process error if you sometimes
        // throw in `makeRequestUntilDone`
        console.info(r.reason);
      }
  }
}

async function makeRequestUntilDone(item: string) {
  while(true){
    try {
      const { data: { data } } = await firstValueFrom(this.httpService.post('https://api.io', { item }))
      return data
    } catch (error) {
      // `throw error` if you don't wanna retry 
      // anymore or it is not a retryable error,
      // otherwise delay and continue
      const retryAfter = error.response.headers['retry-after'] * 1000;
      await delay(retryAfter);
    }
  }
}

function delay(ms: number) {
  return new Promise(function(resolve) {
    setTimeout(resolve, ms);
  });
}

тогда ты можешь

Эй, Тодд, я сделал похожее решение с doWhile, и оно работает, спасибо за помощь!

Natan Deitch 11.05.2022 19:04
Ответ принят как подходящий

Я заставил его работать, используя цикл do while, ожидающий повторной попытки через некоторое время.

async *listMeetings(meetingIds: string[]): AsyncIterableIterator<MeetingResponse> {
  let hasMore = false
  do {
    const res = await Promise.allSettled(meetingIds.map((id) => this.makeRequest(id)))
    const fulfilledRequests = res.filter((r) => r.status === 'fulfilled')
    for (const { value } of fulfilledRequests) {
      yield value
    }
    const rejectedRequest = res.find((r) => r.status === 'rejected')
    const failedMeetings = res.filter((p) => p.status === 'rejected').map(({ reason }: any) => reason.meetingId)
    if (failedMeetings.length === 0 || !rejectedRequest?.reason?.retryAfter) {
      hasMore = false
    } else {
      await new Promise<void>((resolve) => setTimeout(() => resolve(), rejectedRequest.reason.retryAfter))
      yield* this.listMeetings(failedMeetings)
    }
  } while (hasMore)
}

main() {
  for await (const meeting of this.client.listMeetings([])) {
    console.info(meeting)
  }
}

Другие вопросы по теме