摘要:函數(shù)并不是生成器協(xié)程函數(shù)自動(dòng)執(zhí)行的唯一方案。因?yàn)樽詣?dòng)執(zhí)行的關(guān)鍵是,必須有一種機(jī)制,自動(dòng)控制生成器協(xié)程函數(shù)的流程,接收和交還程序的執(zhí)行權(quán)?;卣{(diào)函數(shù)可以做到這一點(diǎn),對(duì)象也可以做到這一點(diǎn)。本系列的下一篇,將介紹基于的實(shí)現(xiàn)的自動(dòng)執(zhí)行器。
PHP下的異步嘗試系列
如果你還不太了解PHP下的生成器和協(xié)程,你可以根據(jù)下面目錄翻閱
PHP下的異步嘗試一:初識(shí)生成器
PHP下的異步嘗試二:初識(shí)協(xié)程
PHP下的異步嘗試三:協(xié)程的PHP版thunkify自動(dòng)執(zhí)行器
PHP下的異步嘗試四:PHP版的Promise
[PHP下的異步嘗試五:PHP版的Promise的繼續(xù)完善]
高階函數(shù)在我們實(shí)現(xiàn)自動(dòng)調(diào)度(器)函數(shù)前,我們先來(lái)理解下高階函數(shù)
thunk函數(shù)# 先求值再傳參 function func(m){ return m * 2; } f(x + 5); // 等同于 # 先傳參再求值 var thunk = function () { return x + 5; }; function func(thunk){ return thunk() * 2; } # 這段我們?cè)趐ython或一些語(yǔ)言里,概念叫高階函數(shù) # 因?yàn)閜hp是解釋性動(dòng)態(tài)語(yǔ)言,所以函數(shù)可以當(dāng)參數(shù)傳入 # 這里python,js,php下函數(shù)都是可以傳參的PHP版本的thunkify函數(shù)
thunkify實(shí)現(xiàn)原理:
包裝一次原始函數(shù)名,然后返回一個(gè)第一次匿名函數(shù)(并攜帶包裝函數(shù)): return function () use ($func){$args = func_get_args();}
然后再獲取該匿名函數(shù)的參數(shù),并在上一次第一次匿名函數(shù)體內(nèi)返回一次帶回調(diào)參數(shù)的第二次匿名函數(shù)(并攜帶上一次環(huán)境上下文): return function ($callback) use ($args, $func){}
調(diào)用包裝函數(shù),參數(shù)為:第一次匿名函數(shù)調(diào)用的參數(shù)+一個(gè)回調(diào)函數(shù)
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" }只能執(zhí)行一次回調(diào)的thunkify函數(shù)
function thunkify($func){ return function () use ($func) { $args = func_get_args(); return function ($callback) use ($args, $func) { // 原本的獲取參數(shù),回調(diào)會(huì)多次執(zhí)行 // array_push($args, $callback); // 增加回調(diào)只能執(zhí)行一次 $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); //我們?cè)黾右淮位卣{(diào) }; $printStrThunkify = thunkify($printStr); $printStrThunkify(...["foo", "bar"])(function (...$p) { var_dump($p); }); # output array(2) { [0]=> string(3) "foo" [1]=> string(3) "bar" }
看到這里,你可能還在疑惑,thunkify函數(shù)其實(shí)只是幫我們包裝了一次有回調(diào)函數(shù)的高階函數(shù)而已
不過(guò)這里到底有什么用處呢,在普通場(chǎng)景下確實(shí)用戶不大(可能用處單純就在做一些前后置函數(shù)包裝也是用處的,類(lèi)似python的裝飾)
但是,但是,但是在生成器協(xié)程里,Thunkify函數(shù)可以用于生成器協(xié)程的自動(dòng)流程管理。
每一次yield出來(lái)的結(jié)果都是一個(gè)thunk函數(shù)的回調(diào)
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(); // 手動(dòng)回調(diào), 模擬自動(dòng)執(zhí)行基礎(chǔ)理解 $value = $gen->current(); $value(function ($p1) use($gen) { $value = $gen->send($p1); $value(function ($p1) use($gen) { $value = $gen->send($p1); var_dump($value); }); });自動(dòng)執(zhí)行器
我們這里只是實(shí)現(xiàn)上面的手動(dòng)回調(diào)執(zhí)行
增加了一個(gè)自動(dòng)執(zhí)行器,把生成器協(xié)程傳入后講自動(dòng)執(zhí)行生成器協(xié)程
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 引入作用域必須帶上&, 否則無(wú)法識(shí)別 $next = function ($p1) use ($gen, &$next) { if (is_null($p1)) { //此處獲取第一次yeild的回調(diào) $result = $gen->current(); } else { // send后返回的是下一次的yield值 $result = $gen->send($p1); } // 是否生成器迭代完成 // 迭代器生成完成,不再迭代執(zhí)行(自動(dòng)執(zhí)行器返回停止) if (!$gen->valid()) { return ; } $result($next); }; $next(null); } $gen1 = gen(); //$gen2 = gen(); autoCaller($gen1); //autoCaller($gen2); # output string(1) "1" string(1) "2" # 如果我們打開(kāi)上面的兩個(gè)sleep()注釋 # output # 等待2秒 string(1) "1" # 等待5秒 string(1) "2" # 因?yàn)檫@里我們的thunk里執(zhí)行的實(shí)際函數(shù)是同步的代碼,所以整體是阻塞的后續(xù)代碼執(zhí)行的總結(jié)
只要執(zhí)行 autoCaller 函數(shù),生成器就會(huì)自動(dòng)迭代完成。這樣一來(lái),異步操作不僅可以寫(xiě)得像同步操作,而且一行代碼就可以執(zhí)行。
Thunkify函數(shù)并不是 生成器協(xié)程 函數(shù)自動(dòng)執(zhí)行的唯一方案。
因?yàn)樽詣?dòng)執(zhí)行的關(guān)鍵是,必須有一種機(jī)制,自動(dòng)控制 生成器協(xié)程 函數(shù)的流程,接收和交還程序的執(zhí)行權(quán)。
回調(diào)函數(shù)可以做到這一點(diǎn),Promise 對(duì)象也可以做到這一點(diǎn)。本系列的下一篇,將介紹基于PHP的Promise實(shí)現(xiàn)的自動(dòng)執(zhí)行器。
附錄參考Thunk 函數(shù)的含義和用法 - 阮一峰
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/30907.html
摘要:如果僅依靠程序自動(dòng)交出控制的話,那么一些惡意程序?qū)?huì)很容易占用全部時(shí)間而不與其他任務(wù)共享。多個(gè)操作可以在重疊的時(shí)間段內(nèi)進(jìn)行。 PHP下的異步嘗試系列 如果你還不太了解PHP下的生成器,你可以根據(jù)下面目錄翻閱 PHP下的異步嘗試一:初識(shí)生成器 PHP下的異步嘗試二:初識(shí)協(xié)程 PHP下的異步嘗試三:協(xié)程的PHP版thunkify自動(dòng)執(zhí)行器 PHP下的異步嘗試四:PHP版的Promise ...
摘要:結(jié)果打印我結(jié)論或問(wèn)題這里我們基礎(chǔ)實(shí)現(xiàn)了一個(gè)可以用于生產(chǎn)環(huán)境的后續(xù)我們會(huì)接續(xù)完善這個(gè)的特有方法,比如等后續(xù)再介紹用實(shí)現(xiàn)的自動(dòng)執(zhí)行器等附錄參考中文對(duì)象入門(mén)阮一峰 PHP下的異步嘗試系列 如果你還不太了解PHP下的生成器和協(xié)程,你可以根據(jù)下面目錄翻閱 PHP下的異步嘗試一:初識(shí)生成器 PHP下的異步嘗試二:初識(shí)協(xié)程 PHP下的異步嘗試三:協(xié)程的PHP版thunkify自動(dòng)執(zhí)行器 PHP下的...
摘要:下的異步嘗試系列下的異步嘗試一初識(shí)生成器下的異步嘗試二初識(shí)協(xié)程下的異步嘗試三協(xié)程的版自動(dòng)執(zhí)行器下的異步嘗試四版的下的異步嘗試五版的的繼續(xù)完善生成器類(lèi)獲取迭代器當(dāng)前值獲取迭代器當(dāng)前值返回當(dāng)前產(chǎn)生的鍵生成器從上一次處繼續(xù)執(zhí)行重置迭代器向生成器中 PHP下的異步嘗試系列 PHP下的異步嘗試一:初識(shí)生成器 PHP下的異步嘗試二:初識(shí)協(xié)程 PHP下的異步嘗試三:協(xié)程的PHP版thunkify自...
摘要:傳統(tǒng)的異步方法回調(diào)函數(shù)事件監(jiān)聽(tīng)發(fā)布訂閱之前寫(xiě)過(guò)一篇關(guān)于的文章,里邊寫(xiě)過(guò)關(guān)于異步的一些概念。內(nèi)部函數(shù)就是的回調(diào)函數(shù),函數(shù)首先把函數(shù)的指針指向函數(shù)的下一步方法,如果沒(méi)有,就把函數(shù)傳給函數(shù)屬性,否則直接退出。 Generator函數(shù)與異步編程 因?yàn)閖s是單線程語(yǔ)言,所以需要異步編程的存在,要不效率太低會(huì)卡死。 傳統(tǒng)的異步方法 回調(diào)函數(shù) 事件監(jiān)聽(tīng) 發(fā)布/訂閱 Promise 之前寫(xiě)過(guò)一篇關(guān)...
摘要:寫(xiě)在前面本文將要實(shí)現(xiàn)一個(gè)順序讀取文件的最優(yōu)方法,實(shí)現(xiàn)方式從最古老的回調(diào)方式到目前的,也會(huì)與大家分享下本人對(duì)于庫(kù)與庫(kù)的理解。其實(shí)的任何異步編程的解決方案的目標(biāo)都是要達(dá)到同步的語(yǔ)義,異步的執(zhí)行。 寫(xiě)在前面 本文將要實(shí)現(xiàn)一個(gè)順序讀取文件的最優(yōu)方法,實(shí)現(xiàn)方式從最古老的回調(diào)方式到目前的async,也會(huì)與大家分享下本人對(duì)于thunk庫(kù)與co庫(kù)的理解。實(shí)現(xiàn)的效果:順序讀取出a.txt與b.txt,將...
閱讀 3761·2021-10-13 09:39
閱讀 3810·2021-09-24 09:48
閱讀 1206·2021-09-01 10:30
閱讀 2537·2019-08-30 15:55
閱讀 1788·2019-08-29 16:39
閱讀 2307·2019-08-26 13:55
閱讀 3063·2019-08-26 12:23
閱讀 1645·2019-08-26 11:59