PHP下的异步尝试系列如果对PHP下的generators和coroutines不是很了解,可以按照以下目录阅读PHP下的异步Attempt1:Get认识PHP下的异步生成器尝试二:认识PHP下的异步过程尝试五:继续完善PHP版本的Promise]高阶函数在我们实现自动调度器(设备)功能之前,我们先来了解一下高阶函数-orderfunctionthunkfunction#先求值再传参functionfunc(m){returnm*2;}f(x+5);//等同于#先传参再求值varthunk=function(){returnx+5;};functionfunc(thunk){returnthunk()*2;}#Thisparagraph我们在python或者一些语言中,这个概念叫做高阶函数#因为php是解释性动态语言,所以函数可以作为参数传入#这里的python,js,php函数都是可以传参的PHP版本thunkify函数thunkify的实现原理:将原函数名包装一次,然后返回一个第一个匿名函数(并携带包装函数):returnfunction()use($func){$args=func_get_args();}然后获取匿名函数Function参数,在最后一个匿名函数体中返回第二个带回调参数的匿名函数(并携带之前的环境上下文):returnfunction($callback)use($args,$func){}调用wrapper函数,参数为:第一个参数匿名函数调用+回调函数functionthunkify($func){returnfunction()use($func){$args=func_get_args();返回函数($callback)使用($args,$func){array_push($args,$callback);返回$func(...$args);};};};$printStr=function($p1,$p2,$callback){$callback($p1,$p2);};$printStrThunkify=thunkify($printStr);$printStrThunkify(...["foo","bar"])(function(...$p){var_dump($p);});#outputarray(2){[0]=>string(3)"foo"[1]=>string(3)"bar"}只能执行回调thunkify函数functionthunkify($func){returnfunction()use($func){$args=func_get_args();returnfunction($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);});返回$func(...$args);};};};$printStr=function($p1,$p2,$callback){$callback($p1,$p2);$回调($p1,$p2);//我们添加一个回调};$printStrThunkify=thunkify($printStr);$printStrThunkify(...["foo","bar"])(函数(...$p){var_dump($p);});#outputarray(2){[0]=>string(3)"foo"[1]=>string(3)"bar"}看到这里,你可能还在疑惑,thunkify函数其实只是包装了一个高阶functionwithacallbackfunctionforus这就是全部,但是这里有什么用呢?确实普通场景用的人不多(可能只是做一些函数前后包装有用,类似python装饰)但是,但是,但是在generator协程中,Thunkify函数可以用于自动生成器协程的流程管理generator协程自动执行的基本理解每次yield的结果都是一个回调函数thunkify($func){returnfunction()use($func){$args=func_get_args();返回函数($callback)使用($args,$func){$callbackCalled=false;array_push($args,function(...$params)use($callback,&$callbackCalled){if($callbackCalled)return;$callbackCalled=true;$callback(...$params);});返回$func(...$args);};};};$printStr1=function($p1,$callback){$callback($p1);};$printStr2=function($p1,$callback){$callback($p1);};$printStrThunkify1=thunkify($printStr1);$printStrThunkify2=thunkify($printStr2);functiongen(){global$printStrThunkify1,$printStrThunkify2;$r1=yield$printStrThunkify1("1");变量转储($r1);$r2=yield$printStrThunkify2("2");var_dump($r2);}$gen=gen();//手动回调,模拟自动执行的基本理解$value=$gen->current();$value(函数($p1)使用($gen){$value=$gen->send($p1);$value(函数($p1)使用($gen){$value=$gen->send($p1);var_dump($value);});});自动执行器这里我们只是实现了上面的手动回调执行,并添加了一个自动执行器,传入生成器协程后,自动执行生成器协程functionthunkify($func){returnfunction()use($func){$args=func_get_args();返回函数($callback)使用($args,$func){$callbackCalled=false;array_push($args,function(...$params)use($callback,&$callbackCalled){if($callbackCalled)return;$callbackCalled=true;$callback(...$params);});返回$func(...$args);};};};$printStr1=function($p1,$callback){睡眠(2);$callback($p1);};$printStr2=function($p1,$callback){睡眠(5);$回调($p1);};$printStrThunkify1=thunkify($printStr1);$printStrThunkify2=thunkify($printStr2);functiongen(){global$printStrThunkify1,$printStrThunkify2;$r1=yield$printStrThunkify1("1");变量转储($r1);$r2=yield$printStrThunkify2("2");var_dump($r2);}functionautoCaller(\Generator$gen){//注意这里的$nextuseimportscope必须带&,否则无法识别$next=function($p1)use($gen,&$next){if(is_null($p1)){//这里获取第一个yield的回调$result=$gen->current();}else{//send后,返回下一个yield值$result=$gen->send($p1);}//是否生成迭代器的迭代完成//迭代器生成完成,不再迭代执行(自动执行器返回停止)if(!$gen->valid()){return;}$结果($下一个);};$next(null);}$gen1=gen();//$gen2=gen();autoCaller($gen1);//autoCaller($gen2);#outputstring(1)"1"string(1)"2"#如果我们打开上面两个sleep()注释#输出#等待2秒string(1)"1"#等待5秒string(1)"2"#因为我们这里的thunk中实际执行的函数是同步代码,so整体是阻塞后续代码执行的总结。只要执行autoCaller函数,生成器就会自动迭代完成。这样,异步操作不仅可以像同步操作那样写,还可以一行代码执行。Thunkify函数并不是生成器协程函数自动化的唯一方式。因为自动执行的关键是必须有一种机制来自动控制生成器协程函数的流程,接收和返回程序的执行权。回调可以做到这一点,Promise对象也可以。本系列的下一篇文章将介绍一个基于PHP的Promise实现的自动执行器。附录参考Thunk函数的含义和用法-阮一峰
