【问题标题】:Error thrown inside generator finishes it despite being caught尽管被捕获,但在生成器中抛出的错误完成了它
【发布时间】:2019-12-18 21:38:54
【问题描述】:

我有一个异步生成器函数,它在内部调用了一些可能引发错误的异步函数。我想要的是,当发生错误时,生成器只是记录它,然后继续进一步工作。所以我有这样的代码......

async * getAll (somestuff) {
  try {
    const thing = await fetchThing()
    const otherThing = await fetchAnother()

    yield {
      ...thing,
      ...otherThing
    }
  } catch (error) {
    console.log('Error happened, but thats ok, I want to continue')
  }
}

但是当错误发生时,它会被 catch 块记录下来,然后生成器产生 { done: true } 并且操作停止。 我曾尝试在 console.log 中的 catch 块之后手动产生 null 但结果相同。

【问题讨论】:

  • 除了 try-catch 之外,生成器没有什么可以做的......你期待什么?

标签: javascript error-handling generator


【解决方案1】:

这种“问题”与生成器本身无关,它仅与 try..catch 块内的 await 机制有关,因为 每当在 try-catch 块内拒绝承诺时, catch 已加入(除非承诺单独尝试捕获)。

事实上,生成器不能再进一步了,因为一旦以某种方式到达catch,它就会一直持续到另一个yield 被调用。如果不需要调用任何东西,它只会完成提供done: true,这就是生成器的预期行为。

您的主要问题是,您希望生成器产生所有值,但它只是不能,因为yield从未达到

try {
    const thing = await fetchThing()
    const otherThing = await fetchAnother()

    yield { // <-- never met if either thing or otherThing are rejected.
      ...thing,
      ...otherThing
    }
  } catch (error) { // <-- this block is reached whenever either thing or otherThing raise an exception.
    console.log('Error happened, but thats ok, I want to continue')
  }

如果您希望 try..catch 块在任一内部可等待元素引发异常时继续,您还需要尝试捕获它们,以便您可以进一步控制它们的“失败”行为:

try {
    let thing, otherThing;
    try {
       thing = await fetchThing()
       otherThing = await fetchAnother()
    }
    catch (innerException) {
       console.log('either of the above failed!', innerException);
    }
    // in this way, the below block will be reached.
    yield {
      ...thing,
      ...otherThing
    }
  } catch (error) {
    console.log('Error happened, but thats ok, I want to continue')
  }

这样,无论两者都失败还是都成功,yield 块将到达并继续执行。

这是一个显示上述内容的示例:

const fakeAsync = async () => await Promise.resolve(true);
const fakeAsyncReject = async () => await Promise.reject(false);

async function* getAll(someStuff) {
  try {
    let res, raiseExc;
    try {
      res = await fakeAsync();
    }
    catch (innerResException) {
      console.log('an inner exception raised.');
    }

    try {
      raiseExc = await fakeAsyncReject();
    }
    catch (innerRaiseException) {
      console.log('an inner exception was raised.', innerRaiseException);
    }
    // yield block, always reached unless there is something really wrong in this try block, like syntax errors or whatever.
    yield {
      res,
      raiseExc
    }
  }
  catch (error) {
    // catch block raised only when the try block above yields an exception, NOT raised when either of the try-catch blocks inside the try-catch actually join the catch.
    console.log('Error happened', error);
  }
}

// Uncomment this block if you want to see how the for-await would work.
/*
(async() => {
for await (var res of getAll([])) {
console.log('res is', res);
}
})();
*/

(async() => {
  const asyncIterator = getAll([]);
  console.log(await asyncIterator.next());
})();

【讨论】:

  • 最后它似乎起作用了,我需要考虑一下并阅读更多内容。非常感谢您解释它@briosheje!还有一件事,如果代码需要到达yield关键字才能继续,我想知道为什么catch() {yield null}不起作用?
  • 不客气,请记住,每个 unhandled 承诺拒绝都会导致该行为,因此即使您正在循环一系列承诺,您也应该处理它;)。不过,希望这能让事情变得清楚。
猜你喜欢
  • 2017-11-07
  • 1970-01-01
  • 2023-03-28
  • 2016-10-27
  • 1970-01-01
  • 2023-01-03
  • 1970-01-01
  • 1970-01-01
  • 2012-04-27
相关资源
最近更新 更多