下面代码中使用一个TaskStack生成器栈,实现了异步方法swoole_timer_after的同步写法;顺带还实现了子生成器的调用;
只要是有callback方法的异步操作,均可以使用一下方式实现异步操作的同步写法;
class TaskStack
{
public $stack;
public $generator;
public $return;
public function __construct(Generator $generator)
{
$this->stack = new SplStack();
$this->generator = $generator;
}
public function run()
{
while (1) {
try {
if (!$this->generator->valid()) {
if (!$this->stack->isEmpty()) {
try {
$this->return = $this->generator->getReturn();
} catch (Exception $e) {
$this->return = null;
}
$this->generator = $this->stack->pop();
/**
* 将子generator里面的返回值赋予父generator当前的yield表达式作为结果值;
* 并且执行下一个父级yield表达式;
* 如果这里不执行下一个父级yield表达式,generator会一直在这个yield,在下面会死循环;
*/
// 下面 子generator执行逻辑分析 中的send方法;
$this->generator->send($this->return);
continue;
}
return;
}
/**
* 子generator执行逻辑分析:
* 1.获取当前yield表达式的结果值result;
* 2.如果result是一个generator生成器;
* 3.那么将当前执行的generator入栈(当前执行的生成器为TaskStack类中的generator属性);
* 4.执行子generator,子generator执行完毕,获取返回值;
* ## 这一刻的send方法作用,巧妙绝伦的设计;
* # 5.send方法将子generator的返回值作为父generator中断的yield表达式的结果值;
* # 6.继续执行父generator;
* ## 如果没有5和6,那么在4执行完毕之后又回到1,死循环;
*/
$this->return = $this->generator->current();
if ($this->return instanceof Generator) {
$this->stack->push($this->generator);
$this->generator = $this->return;
continue;
}
/**
* 异步执行逻辑分析:
* 1.获取当前yield表达式的值result;
* 2.如果result是一个特殊的异步类(自定义的类或者接口用来处理异步操作);
* 3.向异步的回调函数中发送闭包函数Closure,也就是说当异步操作完成,执行回调函数的时候,会执行发送的Closure;
* Closure中操作:
* 1.通过send方法,将异步执行的结果发送到当前执行到的yield表达式作为结果值;
* 2.继续执行TaskStack的run(),继续执行生成器;
* 4.直接结束栈的run()方法;
*
* ## 结合来说也就是说如果遇到一个表达式的值为异步类,
* ## 那么直接结束当前TaskStack的执行,直到异步回调函数执行,才恢复执行;
* ## 注意,这里生成器停止执行,对于进程来说并不是阻塞的,此时进程可以执行别的操作;
*/
if ($this->return instanceof AsyncSSS) {
$this->return->delivery(function () {
echo "time " . time() . PHP_EOL;
$this->generator->send("async return");
$this->run();
});
return;
}
$this->generator->send($this->return);
} catch (Exception $e) {
throw $e;
}
}
}
public function end()
{
return $this->stack->isEmpty() && !$this->generator->valid();
}
}
class AsyncSSS
{
public $second;
public function after($second)
{
$this->second = $second;
return $this;
}
public function delivery(Closure $callback)
{
swoole_timer_after($this->second, $callback);
}
}
function task1()
{
echo "task1 before" . PHP_EOL;
echo "time " . time() . PHP_EOL;
$async = yield (new AsyncSSS)->after(3000);
echo "task1 get async return value : $async" . PHP_EOL;
$generator = yield task1_1();
echo "task1 get task2 return value : $generator" . PHP_EOL;
echo "task1 after" . PHP_EOL;
}
function task1_1()
{
echo "task1_1 before" . PHP_EOL;
$value = yield task1_1_1();
echo "task1_1 get task1_1_1 return value : $value" . PHP_EOL;
echo "task1_1 after" . PHP_EOL;
return 'task1_1 return';
}
function task1_1_1()
{
return "task1_1_1 return";
}
$a = new TaskStack(task1());
$a->run();
echo 'task stack run after' . PHP_EOL;
/**
* 输出:
* task1 before
* time 1511331788
* task stack run after
* time 1511331791
* task1 get async return value : async return
* task1_1 before
* task1_1 get task1_1_1 return value : task1_1_1 return
* task1_1 after
* task1 get task2 return value : task1_1 return
* task1 after
*/