下面代碼中使用一個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
*/