Generator 的异常处理

本文是我在研究 PHP 异步编程时的总结。对于至关多的 PHPer 来讲,可能都不知道 Generator,或者对 Generaotr 的流程不是很熟悉。由于 Generator 使得程序再也不是顺序的。鉴于本人的水平有限,若是有不一样意见,还望指点一二,不胜感激!php

PHP 中的异常处理

从 PHP 5 开始,PHP 为咱们提供了 try catch 来进行异常处理。当咱们使用 catch 将异常捕获,那么一场后续的代码就会执行。咱们看看下面的例子。编程

try {
    throw new Exception('e');
} catch (Exception $e) {
    echo $e->getMessage(); // output: e
}

echo 2; // output: 2

若是咱们没有将异常捕获,那么后面的代码就不会执行了。异步

throw new Exception('e'); // throw an exception

echo 2; // not execute

Generator 的 throw 方法

在 PHP 中,Generator 提供了 throw 方法来抛出异常。用法和普通的异常同样,只不过把 throw 关键字改为了方法调用。异步编程

function gen()
{
    yield 0;
    yield 1;
    yield 2;
    yield 3;
}

$gen = gen();

$gen->throw(new Exception('e')); // throw an exception

var_dump($gen->valid()); // output: false

echo 2; // not execute

一样的,咱们能够这个异常捕获,经过 try catch 来进行。函数

try {
    $gen->throw(new Exception('e'));
} catch (Exception $e) {
    echo $e->getMessage(); // output: e
}

var_dump($gen->valid()); // output: false

echo 2; // output: 2

咱们能够看到,当咱们使用 throw 抛出异常后,当前的生成器的 valid 变成了 false。可是考虑下面一种状况,当咱们在外面调用 throw 方法后,在生成器函数中捕获异常,会发生什么呢?咱们来看下面的例子。工具

function gen()
{
    yield 0;
    try {
        yield;
    } catch (Exception $e) {
        echo $e->getMessage(); // output: e
    }
    yield 2;
    yield 3;
}

$gen = gen();
$gen->next(); // reach the point of catching exception
$gen->throw(new Exception('e'));

var_dump($gen->valid()); // output: true

echo 2; // output: 2

当咱们在生成器函数捕获来自 throw 方法抛出的异常后,生成器依然是 valid 的。可是若是像刚才同样只是在调用 throw 方法,那么生成器就结束了。code

在生成器函数中抛出异常

function gen()
{
    yield 0;
    throw new Exception('e');
    yield 2;
    yield 3;
}

$gen = gen();

$gen->next();

$gen->current(); // throw an exception

var_dump($gen->valid()); // output: false

echo 2; // not execute

以前咱们看到的是调用 throw 方法来抛出异常。那么在生成器函数中,抛出一个异常而没有在生成器函数中捕获,结果也都是同样的。一样的,若是在生成器函数中捕获了异常,那么就和以前的例子同样了。协程

在理解了上面的例子以后,咱们就要考虑一下,若是有嵌套的生成器,会发生什么了。get

嵌套生成器

当咱们在一个生成器函数中,yield 了另一个生成器函数以后,就会变成嵌套生成器。咱们来看下面的例子。io

function subGen()
{
    yield 1;
    throw new Exception('e');
    yield 4;
}

function gen()
{
    yield 0;
    yield subGen();
    yield 2;
    yield 3;
}

$gen = gen();

$gen->next();
$gen->current()->next(); // throw an exception

echo 2; // not execute

对于嵌套的生成器来讲,若是子生成器中抛出了异常,那么在没有捕获这个异常的状况下,会一级一级向上抛出,直到结束。

刚才咱们尝试了,在抛出异常以后,valid 的返回值变成了 false。那么在嵌套生成器中,是否是也是这样呢?咱们把异常捕获,使程序可以继续执行下去,来看下面这个例子。

function subGen()
{
    yield 1;
    throw new Exception('e');
    yield 4;
}

function gen()
{
    yield 0;
    yield subGen();
    yield 2;
    yield 3;
}

$gen = gen();

$gen->next();
try {
    $gen->current()->next();
} catch (Exceprion $e) {
    echo $e->getMessage(); //output: e
}

var_dump($gen->valid()); // output: true

echo 2; // output: 2

因此,当子生成器抛出异常后在迭代的过程当中被正常地捕获,那么,父生成器便不会受到影响,valid 的返回值依然是 true

总结

关于生成器的异常处理,这里来进行一下总结。

  • 在生成器中抛出一个异常,或者使用 throw 方法抛出一个异常,那么,生成器的迭代便会结束,valid 变成 false
  • 在生成器中抛出一个异常,迭代过程当中对异常进行捕获,生成器的迭代依然会结束,valid 依然会变成 false
  • 在生成器中抛出一个异常,在生成器中将其捕获处理,生成器的迭代不会结束,valid 会返回 true
  • 在嵌套的生成器中,若是子生成器抛出了异常,只会对子生成器产生影响,不会对父生成器产生影响。

后记

yield 为咱们提供了使用 PHP 实现半协程的工具。最近在研究使用 yield 实现半协程,而这个过程当中,对异常的处理,是很是重要的。可是 yield 的运行方式决定了异常处理比较难以理解。因而我花了几天的时间,尝试了各类可能,得出来的这些结论。固然因为本人水平有限,若有错误,还望指点一二,不胜感激。

相关文章
相关标签/搜索