zoukankan      html  css  js  c++  java
  • PHP下的异步尝试一:初识生成器

    PHP下的异步尝试系列

    1. PHP下的异步尝试一:初识生成器
    2. PHP下的异步尝试二:初识协程
    3. PHP下的异步尝试三:协程的PHP版thunkify自动执行器
    4. PHP下的异步尝试四:PHP版的Promise
    5. [PHP下的异步尝试五:PHP版的Promise的继续完善]

    生成器类

    
    # http://php.net/manual/zh/class.generator.php
    Generator implements Iterator {
        /* Methods */
        //获取迭代器当前值
        public mixed current ( void )
        //获取迭代器当前值
        public mixed getReturn ( void )
        //返回当前产生的键
        public mixed key ( void )
        //生成器从上一次yield处继续执行
        public void next ( void )
        //重置迭代器
        public void rewind ( void )
        //向生成器中传入一个值
        public mixed send ( mixed $value )
        //向生成器中抛入一个异常
        public mixed throw ( Throwable $exception )
        //检查迭代器是否被关闭
        public bool valid ( void )
        //迭代器序列化时执行的方法
        public void __wakeup ( void )
    }
    

    生成生成器

    尝试实例化类

    
    $gen = new Generator();
    
    # 我们发现不能直接手动实例化
    # output
    PHP Fatal error:  Uncaught Error: The "Generator" class is reserved for internal use and cannot be manually instantiated in /web/www/sxx_admin3/src/cache/test/amphp/gen3.php:8
    

    尝试function方式

    
    function gen($max)
    {
        for ($i=0; $i<$max; $i++) {
            yield $i;
        }
    }
    
    $gen = gen(5);
    
    # success
    # 成功,我们只需要在普通函数方法里yield即可成了生成器
    

    理解php的生成器

    其实各语言都有生成器,比如python,go等

    生成器迭代foreach

    被代码将演示valid, getReturn

    
    function gen($max)
    {
        for ($i=0; $i<$max; $i++) {
            yield $i;
        }
        
        return $max;
    }
    
    $gen = gen(5);
    
    foreach ($gen as $val) {
         var_dump($val);
    }
    
    //如果已经迭代完成,获取返回值
    // php7 支持
    // valid 判断当前迭代器是否迭代完成
    // getReturn 返回迭代器的返回值
    if (version_compare(PHP_VERSION, '7.0.0') >= 0 && !$gen->valid()) {
        var_dump($gen->getReturn());
    }
    

    带key值的生成器迭代foreach

    迭代器返回值可以带key和value,类似

    
    function gen($max)
    {
        for ($i=0; $i<$max; $i++) {
            yield $i => $i+1;
        }
        
        return $max;
    }
    
    $gen = gen(5);
    
    //var_dump($gen->key());
    //var_dump($gen->current());
    
    foreach ($gen as $key=>$val) {
         var_dump($key . "=>" . $val);
    }
    
    # output
    string(4) "0=>1"
    string(4) "1=>2"
    string(4) "2=>3"
    string(4) "3=>4"
    string(4) "4=>5"
    

    生成器迭代手动迭代

    本代码将演示rewind, next, send方法

    
    function gen($max)
    {
        for ($i=0; $i<$max; $i++) {
            // 此处的(yield $i)在php7以后版本可省略
            $res = (yield $i);
            var_dump($res);
        }
    
        return $max;
    }
    
    $gen = gen(10);
    
    // 可不调用,隐式调用
    // 如果迭代开始后不能再rewind(即使用了next或send后)
    $gen->rewind();
    
    // 打印获取到当前生成器的值
    var_dump("1::" . $gen->current()); //output: string(4) "1::0"
    
    // 下面2句代码执行,将返回错误
    // $gen->next();
    // $gen->rewind();
    
    //继续执行,知道遇到下一个yield
    $gen->next();
    var_dump("2::" . $gen->current()); //output: string(4) "2::1"
    $gen->next();
    var_dump("3::" . $gen->current()); //output: string(4) "3::2"
    
    // send传null值等同于调用next(本方法尝试来自python的迭代器,成功)
    $gen->send(null);
    var_dump("4::" . $gen->current()); //output: string(4) "4::3"
    
    // send传值会也会继续执行
    $gen->send(100);
    var_dump("5::" . $gen->current()); //output: string(4) "5::4"
    
    
    //如果已经迭代完成,获取返回值
    // php7 支持
    if (version_compare(PHP_VERSION, '7.0.0') >= 0 && !$gen->valid()) {
        var_dump($gen->getReturn());
    }
    
    # output:
    string(4) "1::0"
    NULL
    string(4) "2::1"
    NULL
    string(4) "3::2"
    NULL
    string(4) "4::3"
    int(100)
    string(4) "5::4"
    
    # 我们先不去理会gen里var_dump输出的NULL或int(100)
    # 我们先去理解每次next后current可以获取到当前yield的值即可
    

    尝试理解send输出

    
    function gen($max)
    {
        for ($i=0; $i<$max; $i++) {
            $res = (yield $i);
            var_dump($res);
        }
    
        return $max;
    }
    
    $gen = gen(10);
    
    var_dump("1::" . $gen->current());
    
    $gen->send(222);
    var_dump("2::" . $gen->current());
    
    $gen->send(333);
    var_dump("3::" . $gen->current());
    
    $gen->send(null);
    var_dump("4::" . $gen->current());
    
    
    # output:
    string(4) "1::0"
    int(222)
    string(4) "2::1"
    int(333)
    string(4) "3::2"
    int(444)
    string(4) "4::3"
    
    # send和next
    # next() => current = yield值
    # send(val) $rs = yield 表达式执行 = val; //send这样理解即可
    # 在当前某个yield处时send,当前yield表达式处返回,如果没有变量接收,那么继续下一个yield处返回
    $rs = (yield  somethind_to_do(...) );
     ^           |-------------------|
     |                  yield值
     |    |----------------------------|
     |               yield 表达式
    yield表达式结果
    
    
    # 执行顺序流程类似
    $res = (yield 1);   // <- var_dump("1::" . $gen->current()); 第一步到yield返回
    
    var_dump($res);     // <- $gen->send(222); 第二步send:222后,继续往下走$res=222 然后var_dump($res), 然后到了yield 2
    $res = (yield 2);   // <- var_dump("2::" . $gen->current());  打印当前的值2
    
    var_dump($res);     // <- $gen->send(333); 第三步send:333后,继续往下走$res=333 然后var_dump($res), 然后到了yield 3
    $res = (yield 3);   // <- var_dump("3::" . $gen->current());
    
    var_dump($res);     // <- $gen->send(null); 第二步send:null后,继续往下走$res=null 然后var_dump($res), 然后到了yield 4
    $res = (yield 4);   // <- var_dump("4::" . $gen->current());
    

    生成器throw抛出错误

    
    # 内部定义异常并返回,外部接收
    function gen() {
        echo "Gen 开始
    ";
        yield new Exception('内部定义异常');
        echo "Gen 结束
    ";
    }
    
    $gen = gen();
    try {
        throw $gen->current();
    } catch (Exception $e) {
        echo "外部捕获异常:" . $e->getMessage() . PHP_EOL;
    }
    
    $gen->send("123");
    
    # output: 
    Gen 开始
    外部捕获异常:内部定义异常
    Gen 结束
    
    
    # 内部接收send传入的异常,然后直接throw,外部接收
    function gen() {
        echo "Gen 开始
    ";
        throw (yield new Exception('内部定义异常'));
        echo "Gen 结束
    ";
    }
    
    $gen = gen();
    try {
        throw $gen->current();
    } catch (Exception $e) {
        echo "外部捕获异常:" . $e->getMessage() . PHP_EOL;
    }
    
    try {
        $gen->send(new Exception("外部定义异常"));
    } catch (Exception $e) {
        echo "外部捕获异常:" . $e->getMessage() . PHP_EOL;
    }
    
    # output
    Gen 开始
    外部捕获异常:内部定义异常
    外部捕获异常:外部定义异常
    
    # 
    function gen() {
        echo "Gen 开始
    ";
        try {
            yield new Exception('内部定义异常');
        } catch (Exception $e) {
            echo "内部捕获异常:" . "Exception: {$e->getMessage()}
    ";
        }
    
        echo "Gen 结束
    ";
    }
    
    $gen = gen();
    try {
        throw $gen->current();
    } catch (Exception $e) {
        echo "外部捕获异常:" . $e->getMessage() . PHP_EOL;
    }
    
    // 注意这里是throw方法,相当于
    // $gen->send(new Exception("外部定义异常"));
    // throw yield (yield接收 = new Exception("外部定义异常"))
    $gen->throw(new Exception("外部定义异常"));
    
    #output 
    Gen 开始
    外部捕获异常:内部定义异常
    内部捕获异常:Exception: 外部定义异常
    Gen 结束
    

    总结

    
    初识我们只需要先理解next和send即可
    next->让我们可以主动自动执行迭代器
    send->可以让我们的迭代器实现双向通信,改变执行体流程顺序
    后续我们会介绍使用场景和Co自动执行体等
    

    文章更名记录

    
    2018.09.20: [PHP下的生成器尝试一:初识PHP下的生成器] -> [PHP下的异步尝试一:初识生成器]
    

    原文地址:https://segmentfault.com/a/1190000016444494

  • 相关阅读:
    alternatives命令用法
    Apache Kafka官方文档翻译(原创)
    java===泛型
    java===map集合
    java===集合框架之HashSet,treeSet
    java===集合之linkedlist
    java===集合框架之list体系
    StringBuilder===练习
    java===String类练习
    java练习多线程
  • 原文地址:https://www.cnblogs.com/lalalagq/p/9969963.html
Copyright © 2011-2022 走看看