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