javascript 在循环的for...中重试失败的API调用

shyt4zoc  于 2023-02-18  发布在  Java
关注(0)|答案(2)|浏览(162)

我正在尝试在Nottion JS SDK的iteratePaginatedAPI上构建一个 Package 器来处理错误。我特别不知道如何捕捉API错误,以便能够实际重试它们(又名重试失败的迭代)。以下是我的尝试:

async function* queryNotion(listFn, firstPageArgs) {
  try {
    for await (const result of iteratePaginatedAPI(listFn, firstPageArgs)) {
      yield* result
    }
  } catch (error) {
    if (error.code === APIErrorCode.RateLimited) {
      console.log('rate_limited');
      console.log(error);
      sleep(1);
      // How would I retry the last iteration?
    }
  }
}

来自Ruby世界,在一个rescue块中有一个retry。任何帮助都将不胜感激!

z9smfwbn

z9smfwbn1#

非常有趣的问题。问题是异常来自for await本身,而不是它的主体,所以你不能在那里捕获它。当异常命中时,循环结束。
注意,迭代器可能是在拒绝/异常之后完成的,在这种情况下,除了启动一个新的迭代器之外,您什么也做不了。
也就是说,你总是可以自己调用Iterator.next()并手动处理结果,异步迭代器的next()调用将返回一个类似{value: Promise<any>, done: boolean}的对象,当在循环中运行它时,你可以等待try..catch中的承诺,只有当done变为true时才退出循环:

async function* queryNotion(listFn, firstPageArgs) {
  const asyncGenerator = mockIteratePaginatedAPI(listFn, firstPageArgs)
  while (true) {
    const current = asyncGenerator.next()
    if (current.done) {
      break
    }
    try {
      yield* await current.value
    } catch (e) {
      console.log(`got exception: "${e}" - trying again`)
      continue
    }
  }
}

function* mockIteratePaginatedAPI(a, b) {
  for (let i = 0; i < 8; i++) {
    yield new Promise((resolve, reject) => setTimeout(() => [3, 5].includes(i) ? reject(`le error at ${i}`) : resolve([i]), 500))
  }
}

(async function() {
  for await (const n of queryNotion('foo', 'bar')) {
    console.log(n)
  }
})()

如果我们保留对生成器的引用,我们也可以把它放回到for async中,这可能更容易阅读,但是for await ...of会在提前退出循环时调用迭代器的return(),很可能是结束循环,在这种情况下,将不起作用

async function* queryNotion(listFn, firstPageArgs) {
  const asyncGenerator = mockIteratePaginatedAPI(listFn, firstPageArgs)
  while (true) {
    try {
      for await (const result of asyncGenerator) {
        yield* result
      }
      break
    } catch (e) {
      console.log('got exception:', e, 'trying again')
    }
  }
}

function* mockIteratePaginatedAPI(a, b) {
  for (let i = 0; i < 8; i++) {
    yield new Promise((resolve, reject) => setTimeout(() => [3, 5].includes(i) ? reject(`le error at ${i}`) : resolve([i]), 500))
  }
}

(async function () {
  for await (const n of queryNotion('foo', 'bar')) {
    console.log(n)
  }
})()
6tr1vspr

6tr1vspr2#

只需在if中添加continue语句

async function* queryNotion(listFn, firstPageArgs) {
  try {
    for await (const result of iteratePaginatedAPI(listFn, firstPageArgs)) {
      yield* result
    }
  } catch (error) {
    if (error.code === APIErrorCode.RateLimited) {
      console.log('rate_limited');
      console.log(error);
      await sleep(1);
      continue; // retry the last iteration
    }
  }
}

相关问题