繁体   English   中英

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

[英]How to catch exception thrown from a generator and resume iteration?

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

为了说明这一点,下面是一个生成器的例子,它会产生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 ,我希望生成器继续通过 Exception 以便我可以获得3

在生成器中抛出异常会完全关闭它,这就是它在第三次迭代时返回“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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM