摘要:函数并不是生成器协程函数自动执行的唯一方案。因为自动执行的关键是,必须有一种机制,自动控制生成器协程函数的流程,接收和交还程序的执行权。回调函数可以做到这一点,对象也可以做到这一点。本系列的下一篇,将介绍基于的实现的自动执行器。
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 函数的含义和用法 - 阮一峰
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/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 之前写过一篇关...
阅读 3722·2021-10-13 09:39
阅读 3787·2021-09-24 09:48
阅读 1188·2021-09-01 10:30
阅读 2524·2019-08-30 15:55
阅读 1772·2019-08-29 16:39
阅读 2294·2019-08-26 13:55
阅读 3048·2019-08-26 12:23
阅读 1632·2019-08-26 11:59