yield协程

1、Generator 
Generator , 一种可以返回迭代器的生成器,当程序运行到yield的时候,当前程序就唤起协程记录上下文,然后主函数继续操作,当需要操作的时候,在通过迭代器的next重新调起

function xrange($start, $end, $step = 1) {  
        for ($i = $start; $i <= $end; $i += $step) {  
            yield $i;  
    }  
}  
foreach (xrange(1, 1000) as $num) {  
        echo $num, "\n";  
}  
/* 
 * 1 
 * 2 
 * ... 
 * 1000 
 */  

如果了解过迭代器的朋友,就可以通过上面这一段代码看出Generators的运行流程

Generators::rewind() 重置迭代器
Generators::valid() 检查迭代器是否被关闭
Generators::current() 返回当前产生的值
Generators::next() 生成器继续执行
Generators::valid() 
Generators::current() 
Generators::next() 
 ...
Generators::valid() 直到返回 false 迭代结束

2、Generator应用 
很多不了解的朋友看完可能会表示这有什么用呢?

举个栗子: 
比如从数据库取出数亿条数据,这个时候要求用一次请求加响应返回所有值该怎么办呢?获取所有值,然后输出,这样肯定不行,因为会造成PHP内存溢出的,因为数据量太大了。如果这时候用yield就可以将数据分段获取,理论上这样是可以取出无限的数据的。

一般的获取方式 :

数据库连接.....
$sql = "select * from `user` limit 0,500000000";
$stat = $pdo->query($sql);
$data = $stat->fetchAll();  //mysql buffered query遍历巨大的查询结果导致的内存溢出
var_dump($data);

yield获取方式:

数据库连接.....
function get(){
    $sql = "select * from `user` limit 0,500000000";
    $stat = $pdo->query($sql);
    while ($row = $stat->fetch()) {
        yield $row;
    }
}
foreach (get() as $row) {
        var_dump($row);
}

3、Generator::send

  1. 向生成器中传入一个值,并且当做 yield 表达式的结果,然后继续执行生成器。
  2. 如果当这个方法被调用时,生成器不在 yield 表达式,那么在传入值之前,它会先运行到第一个 yield 表达式。As such it is not necessary to prime PHP generators with a Generator::next() call (like it is done in Python)

这代表了什么,这代表了我们可以使用yield进行双向通信

再举个栗子

$ben = call_user_func(function (){
        $hello = (yield 'my name is ben ,what\'s your name'.PHP_EOL);
        echo $hello;
});
$sayHello = $ben->current();
echo $sayHello;
    $ben->send('hi ben ,my name is alex');
/* 
 * output
 * 
 * my name is ben ,what's your name
 * hi ben ,my name is alex 
 */  

这样ben跟alex他们两个就实现了一次相互问好,在这个例子中我们可以发现,yield跟以往的return不同,它不仅可以返回数据,还可以获取外部返回的数据

而且不仅仅能够send,PHP还提供了一个throw,允许我们返回一个异常给Generator

$Generatorg = call_user_func(function(){
        $hello = (yield '[yield] say hello'.PHP_EOL);
        echo $hello.PHP_EOL;
        try{
            $jump = (yield '[yield] I jump,you jump'.PHP_EOL);
        }catch(Exception $e){
            echo '[Exception]'.$e->getMessage().PHP_EOL;
    }
});
$hello = $Generatorg->current();
echo $hello;
    $jump = $Generatorg->send('[main] say hello');
    echo $jump;
$Generatorg->throw(new Exception('[main] No,I can\'t jump'));
/*
 * output
 *
 * [yield] say hello
 * [main] say hello
 * [yield] I jump,you jump
 * [Exception][main] No,I can't jump
 */

 

posted on 2018-01-09 10:24  weblee  阅读(378)  评论(0)    收藏  举报