若是你還不太瞭解PHP下的生成器和協程,你能夠根據下面目錄翻閱php
在咱們實現自動調度(器)函數前,咱們先來理解下高階函數html
# 先求值再傳參 function func(m){ return m * 2; } f(x + 5); // 等同於 # 先傳參再求值 var thunk = function () { return x + 5; }; function func(thunk){ return thunk() * 2; } # 這段咱們在python或一些語言裏,概念叫高階函數 # 由於php是解釋性動態語言,因此函數能夠當參數傳入 # 這裏python,js,php下函數都是能夠傳參的
thunkify實現原理:python
function thunkify($func){ return function () use ($func) { $args = func_get_args(); return function ($callback) use ($args, $func) { array_push($args, $callback); return $func(...$args); }; }; }; $printStr = function($p1, $p2, $callback) { $callback($p1, $p2); }; $printStrThunkify = thunkify($printStr); $printStrThunkify(...["foo", "bar"])(function (...$p) { var_dump($p); }); # output array(2) { [0]=> string(3) "foo" [1]=> string(3) "bar" }
function thunkify($func){ return function () use ($func) { $args = func_get_args(); return function ($callback) use ($args, $func) { // 本來的獲取參數,回調會屢次執行 // array_push($args, $callback); // 增長回調只能執行一次 $callbackCalled = false; array_push($args, function (...$params) use ($callback, &$callbackCalled) { if ($callbackCalled) return ; $callbackCalled = true; $callback(...$params); }); return $func(...$args); }; }; }; $printStr = function($p1, $p2, $callback) { $callback($p1, $p2); $callback($p1, $p2); //咱們增長一次回調 }; $printStrThunkify = thunkify($printStr); $printStrThunkify(...["foo", "bar"])(function (...$p) { var_dump($p); }); # output array(2) { [0]=> string(3) "foo" [1]=> string(3) "bar" }
看到這裏,你可能還在疑惑,thunkify函數其實只是幫咱們包裝了一次有回調函數的高階函數而已
不過這裏到底有什麼用處呢,在普通場景下確實用戶不大(可能用處單純就在作一些先後置函數包裝也是用處的,相似python的裝飾)
可是,可是,可是在生成器協程
裏,Thunkify函數
能夠用於生成器協程
的自動流程管理。segmentfault
每一次yield出來的結果都是一個thunk函數的回調異步
function thunkify($func){ return function () use ($func) { $args = func_get_args(); return function ($callback) use ($args, $func) { $callbackCalled = false; array_push($args, function (...$params) use ($callback, &$callbackCalled) { if ($callbackCalled) return ; $callbackCalled = true; $callback(...$params); }); return $func(...$args); }; }; }; $printStr1 = function($p1, $callback) { $callback($p1); }; $printStr2 = function($p1, $callback) { $callback($p1); }; $printStrThunkify1 = thunkify($printStr1); $printStrThunkify2 = thunkify($printStr2); function gen() { global $printStrThunkify1, $printStrThunkify2; $r1 = yield $printStrThunkify1("1"); var_dump($r1); $r2 = yield $printStrThunkify2("2"); var_dump($r2); } $gen = gen(); // 手動回調, 模擬自動執行基礎理解 $value = $gen->current(); $value(function ($p1) use($gen) { $value = $gen->send($p1); $value(function ($p1) use($gen) { $value = $gen->send($p1); var_dump($value); }); });
咱們這裏只是實現上面的手動回調執行
增長了一個自動執行器,把生成器協程傳入後講自動執行生成器協程函數
function thunkify($func){ return function () use ($func) { $args = func_get_args(); return function ($callback) use ($args, $func) { $callbackCalled = false; array_push($args, function (...$params) use ($callback, &$callbackCalled) { if ($callbackCalled) return ; $callbackCalled = true; $callback(...$params); }); return $func(...$args); }; }; }; $printStr1 = function($p1, $callback) { sleep(2); $callback($p1); }; $printStr2 = function($p1, $callback) { sleep(5); $callback($p1); }; $printStrThunkify1 = thunkify($printStr1); $printStrThunkify2 = thunkify($printStr2); function gen() { global $printStrThunkify1, $printStrThunkify2; $r1 = yield $printStrThunkify1("1"); var_dump($r1); $r2 = yield $printStrThunkify2("2"); var_dump($r2); } function autoCaller(\Generator $gen) { // 注意這裏的$next use 引入做用域必須帶上&, 不然沒法識別 $next = function ($p1) use ($gen, &$next) { if (is_null($p1)) { //此處獲取第一次yeild的回調 $result = $gen->current(); } else { // send後返回的是下一次的yield值 $result = $gen->send($p1); } // 是否生成器迭代完成 // 迭代器生成完成,再也不迭代執行(自動執行器返回中止) if (!$gen->valid()) { return ; } $result($next); }; $next(null); } $gen1 = gen(); //$gen2 = gen(); autoCaller($gen1); //autoCaller($gen2); # output string(1) "1" string(1) "2" # 若是咱們打開上面的兩個sleep()註釋 # output # 等待2秒 string(1) "1" # 等待5秒 string(1) "2" # 由於這裏咱們的thunk裏執行的實際函數是同步的代碼,因此總體是阻塞的後續代碼執行的
只要執行 autoCaller
函數,生成器就會自動迭代完成。這樣一來,異步操做不只能夠寫得像同步操做,並且一行代碼就能夠執行。code
Thunkify函數並非 生成器協程
函數自動執行的惟一方案。協程
由於自動執行的關鍵是,必須有一種機制,自動控制 生成器協程
函數的流程,接收和交還程序的執行權。htm
回調函數能夠作到這一點,Promise 對象也能夠作到這一點。本系列的下一篇,將介紹基於PHP的Promise
實現的自動執行器。對象