若是你还不太了解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
实现的自动执行器。对象