【问题标题】:How to catch exception thrown from a generator and resume iteration?如何捕获生成器抛出的异常并恢复迭代?
【发布时间】:2018-11-22 19:14:35
【问题描述】:

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

为了说明这一点,下面是一个生成器示例,它将产生 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++。

标签: php exception generator


【解决方案1】:

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

所有在生成器内部抛出的异常也应该在生成器内部捕获和处理,您甚至可以将它们从外部扔到生成器中使用$g-&gt;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

【讨论】:

  • 虽然这可行,但它是否在某处记录了这是在生成器中引发异常的预期行为?
  • @ChadSikorra 检查第二段:“你应该在生成器中处理异常,你甚至可以使用 $g->throw() 方法将它们扔到生成器中。更多信息check the documentation"
猜你喜欢
  • 2011-03-01
  • 2023-03-07
  • 1970-01-01
  • 1970-01-01
  • 2019-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多