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

时间:2018-06-13 09:06:16

标签: php exception generator

我有一个生成器,它将一组值传递给一个方法并生成结果。调用的方法可能会返回异常。当发生这种情况时,我希望异常通过调用生成器来处理异常的代码,然后继续循环生成器。

为了说明这一点,以下是生成 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,我希望生成器继续超过异常,以便我可以获得 {{1 }}

1 个答案:

答案 0 :(得分:2)

在生成器中抛出异常完全关闭它,这就是它在第三次迭代时返回“NULL”的原因。如果您在抛出异常后尝试$g->valid(),则会得到false作为结果。

您应该处理生成器内的异常,甚至可以使用$g->throw()方法将它们放在生成器中。有关详细信息check the documentation

然而,您可以尝试实现的目标。您可以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