摘要:函數并不是生成器協程函數自動執行的唯一方案。因為自動執行的關鍵是,必須有一種機制,自動控制生成器協程函數的流程,接收和交還程序的執行權。回調函數可以做到這一點,對象也可以做到這一點。本系列的下一篇,將介紹基于的實現的自動執行器。
PHP下的異步嘗試系列
如果你還不太了解PHP下的生成器和協程,你可以根據下面目錄翻閱
PHP下的異步嘗試一:初識生成器
PHP下的異步嘗試二:初識協程
PHP下的異步嘗試三:協程的PHP版thunkify自動執行器
PHP下的異步嘗試四:PHP版的Promise
[PHP下的異步嘗試五:PHP版的Promise的繼續完善]
高階函數在我們實現自動調度(器)函數前,我們先來理解下高階函數
thunk函數# 先求值再傳參 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下函數都是可以傳參的PHP版本的thunkify函數
thunkify實現原理:
包裝一次原始函數名,然后返回一個第一次匿名函數(并攜帶包裝函數): return function () use ($func){$args = func_get_args();}
然后再獲取該匿名函數的參數,并在上一次第一次匿名函數體內返回一次帶回調參數的第二次匿名函數(并攜帶上一次環境上下文): return function ($callback) use ($args, $func){}
調用包裝函數,參數為:第一次匿名函數調用的參數+一個回調函數
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" }只能執行一次回調的thunkify函數
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函數可以用于生成器協程的自動流程管理。
每一次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 函數,生成器就會自動迭代完成。這樣一來,異步操作不僅可以寫得像同步操作,而且一行代碼就可以執行。
Thunkify函數并不是 生成器協程 函數自動執行的唯一方案。
因為自動執行的關鍵是,必須有一種機制,自動控制 生成器協程 函數的流程,接收和交還程序的執行權。
回調函數可以做到這一點,Promise 對象也可以做到這一點。本系列的下一篇,將介紹基于PHP的Promise實現的自動執行器。
附錄參考Thunk 函數的含義和用法 - 阮一峰
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/30907.html
摘要:如果僅依靠程序自動交出控制的話,那么一些惡意程序將會很容易占用全部時間而不與其他任務共享。多個操作可以在重疊的時間段內進行。 PHP下的異步嘗試系列 如果你還不太了解PHP下的生成器,你可以根據下面目錄翻閱 PHP下的異步嘗試一:初識生成器 PHP下的異步嘗試二:初識協程 PHP下的異步嘗試三:協程的PHP版thunkify自動執行器 PHP下的異步嘗試四:PHP版的Promise ...
摘要:結果打印我結論或問題這里我們基礎實現了一個可以用于生產環境的后續我們會接續完善這個的特有方法,比如等后續再介紹用實現的自動執行器等附錄參考中文對象入門阮一峰 PHP下的異步嘗試系列 如果你還不太了解PHP下的生成器和協程,你可以根據下面目錄翻閱 PHP下的異步嘗試一:初識生成器 PHP下的異步嘗試二:初識協程 PHP下的異步嘗試三:協程的PHP版thunkify自動執行器 PHP下的...
摘要:下的異步嘗試系列下的異步嘗試一初識生成器下的異步嘗試二初識協程下的異步嘗試三協程的版自動執行器下的異步嘗試四版的下的異步嘗試五版的的繼續完善生成器類獲取迭代器當前值獲取迭代器當前值返回當前產生的鍵生成器從上一次處繼續執行重置迭代器向生成器中 PHP下的異步嘗試系列 PHP下的異步嘗試一:初識生成器 PHP下的異步嘗試二:初識協程 PHP下的異步嘗試三:協程的PHP版thunkify自...
摘要:傳統的異步方法回調函數事件監聽發布訂閱之前寫過一篇關于的文章,里邊寫過關于異步的一些概念。內部函數就是的回調函數,函數首先把函數的指針指向函數的下一步方法,如果沒有,就把函數傳給函數屬性,否則直接退出。 Generator函數與異步編程 因為js是單線程語言,所以需要異步編程的存在,要不效率太低會卡死。 傳統的異步方法 回調函數 事件監聽 發布/訂閱 Promise 之前寫過一篇關...
閱讀 3743·2021-10-13 09:39
閱讀 3799·2021-09-24 09:48
閱讀 1198·2021-09-01 10:30
閱讀 2529·2019-08-30 15:55
閱讀 1783·2019-08-29 16:39
閱讀 2300·2019-08-26 13:55
閱讀 3053·2019-08-26 12:23
閱讀 1639·2019-08-26 11:59