如何捕获生成器抛出的异常并恢复迭代?

6
我有一个生成器,它将一组数值传递给一个方法并产生结果。被调用的方法可能会返回异常。当发生这种情况时,我希望异常能够传递到调用生成器的代码中处理异常,然后继续循环生成器。
为了说明这一点,以下是一个示例生成器,它将产生1,抛出\Exception,然后产生3
/** @var \Generator $gen */
$gen = function () {
    for ($i = 1; $i <= 3; $i++) {
        if ($i == 2) {
            throw new \Exception('Exception thrown for 2');
        }

        yield $i;
    }
};

这是我尝试运行此代码的示例,以便使其产生3

$g = $gen();

var_export($g->current());
echo "\n";

try {
    $g->next();
    var_export($g->current());
    echo "\n";
} catch (\Exception $e) {
    echo $e->getMessage() . "\n";
}

try {
    $g->next();
    var_export($g->current());
    echo "\n";
} catch (\Exception $e) {
    echo $e->getMessage() . "\n";
}

以下是上述代码的输出结果。
1
Exception thrown for 2.
NULL

因此,对next()的重复调用无效,并且current()将返回NULL,我希望生成器在异常之后继续运行,以便我可以得到3


你可以尝试在条件语句中手动执行计数器增量操作,在异常抛出之前执行 i++。 - Naveed Ahmed
1个回答

6
在生成器内部抛出异常会完全关闭它,这就是为什么在第三次迭代时返回“NULL”的原因。如果在抛出异常后尝试使用$g->valid(),则将得到false作为结果。
所有在生成器内部抛出的异常也应该被捕获并处理,你甚至可以使用$g->throw()方法从外部向生成器中抛出异常进行处理。有关更多信息,请参阅文档
然而,你想要实现的目标是可行的。你可以用yield代替抛出异常。这样你就不会关闭生成器,并且可以在外部处理异常。
请尝试以下代码:
$gen = function () {
    for ($i = 1; $i <= 3; $i++) {
        // If something wrong happens
        if ($i == 2) {
            // Instead throwing the exception yield it
            // that way we don't close the generator
            yield new \Exception('Exception thrown for 2');
        } else {
            yield $i;
        }
    }
};

并使用以下方式进行测试:

$g = $gen();
for ($i = 0; $i < 3; $i++) {
  $current = $g->current();
  
  // Instead of catching, check if the yielded value is Exception
  if ($current instanceof \Exception) {
      // Handle the exception directly
      // or throw it with throw $current to handle it in normal try-catch block
      echo $current->getMessage() . "\n";
  } else {
      echo $current . "\n";
  }
  
  $g->next();
}

提供给你结果:

1
Exception thrown for 2
3

虽然这样可以工作,但有没有记录表明在生成器内部抛出异常是预期行为? - ChadSikorra
@ChadSikorra 请查看第二段: "您应该在生成器内部处理异常,甚至可以使用 $g->throw() 方法在生成器内部抛出异常。更多信息请查看文档" - Wilq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接