PHP協程(2):通過生成器棧實現異步的同步寫法

下面代碼中使用一個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
 */
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章