PHP下的异步尝试系列

如果你还不太了解PHP下的生成器和协程,你可以根据下面目录翻阅

  1. PHP下的异步尝试一:初识生成器
  2. PHP下的异步尝试二:初识协程
  3. PHP下的异步尝试三:协程的PHP版thunkify自动执行器
  4. PHP下的异步尝试四:PHP版的Promise
  5. [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实现原理:

  1. 包装一次原始函数名,然后返回一个第一次匿名函数(并携带包装函数): return function () use ($func){$args = func_get_args();}
  2. 然后再获取该匿名函数的参数,并在上一次第一次匿名函数体内返回一次带回调参数的第二次匿名函数(并携带上一次环境上下文): return function ($callback) use ($args, $func){}
  3. 调用包装函数,参数为:第一次匿名函数调用的参数+一个回调函数

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 函数的含义和用法 - 阮一峰

原文地址:

PHP下的异步尝试三:协程的PHP版thunkify自动执行器的更多相关文章

  1. PHP下的异步尝试二:初识协程

    PHP下的异步尝试系列 如果你还不太了解PHP下的生成器,你可以根据下面目录翻阅 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunkify ...

  2. PHP下的异步尝试四:PHP版的Promise

    PHP下的异步尝试系列 如果你还不太了解PHP下的生成器和协程,你可以根据下面目录翻阅 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunk ...

  3. PHP下的异步尝试一:初识生成器

    PHP下的异步尝试系列 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunkify自动执行器 PHP下的异步尝试四:PHP版的Promise ...

  4. 进程&线程(三):外部子进程subprocess、异步IO、协程、分布式进程

    1.外部子进程subprocess python之subprocess模块详解--小白博客 - 夜风2019 - 博客园 python subprocess模块 - lincappu - 博客园 之前 ...

  5. Python异步IO之协程(一):从yield from到async的使用

    引言:协程(coroutine)是Python中一直较为难理解的知识,但其在多任务协作中体现的效率又极为的突出.众所周知,Python中执行多任务还可以通过多进程或一个进程中的多线程来执行,但两者之中 ...

  6. (并发编程)进程池线程池--提交任务2种方式+(异步回调)、协程--yield关键字 greenlet ,gevent模块

    一:进程池与线程池(同步,异步+回调函数)先造个池子,然后放任务为什么要用“池”:池子使用来限制并发的任务数目,限制我们的计算机在一个自己可承受的范围内去并发地执行任务池子内什么时候装进程:并发的任务 ...

  7. Python的异步编程[0] -> 协程[1] -> 使用协程建立自己的异步非阻塞模型

    使用协程建立自己的异步非阻塞模型 接下来例子中,将使用纯粹的Python编码搭建一个异步模型,相当于自己构建的一个asyncio模块,这也许能对asyncio模块底层实现的理解有更大的帮助.主要参考为 ...

  8. day37 异步回调和协程

    异步回调 """ 异步任务使用场景 爬虫 1.从目标站点下载网页数据 本质就是HTML格式字符串 2.用re从字符串中提取出你需要的数据 ""&quo ...

  9. 异步IO(协程,消息循环队列)

    同步是CPU自己主动查看IO操作是否完成,异步是IO操作完成后发出信号通知CPU(CPU是被通知的) 阻塞与非阻塞的区别在于发起IO操作之后,CPU是等待IO操作完成再进行下一步操作,还是不等待去做其 ...

随机推荐

  1. 训练1-P

    一个矩形的面积为S,已知该矩形的边长都是整数,求所有满足条件的矩形中,周长的最小值. 例如:S = 24,那么有{1 24} {2 12} {3 8} {4 6}这4种矩形,其中{4 6}的周长最小, ...

  2. mknod指令详解

    mknod - make block or character special filesmknod [OPTION]... NAME TYPE [MAJOR MINOR]    option 有用的 ...

  3. VUEX 总结

    What is Vuex? vuex是一个专为Vue.js应用程序开发的状态管理模式.他采用集中式储存管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变换 VUEX并不限制你的代 ...

  4. java 线程安全和不安全

    线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用.不会出现数据不一致或者数据污染.(Vector,HashTa ...

  5. mybatis逆向工程不生成Example

    mybatis逆向生成映射文件时会生成一大堆example文件,没感觉有啥用,可以手动删除这些多余的东西,使项目变得好看许多 也可以通过配置达到目的: 原配置: <table tableName ...

  6. Python Study (06)内存管理GC

    对象在内存的存储,我们可以求助于Python的内置函数id().它用于返回对象的身份(identity).其实,这里所谓的身份,就是该对象的内存地址. a = 1 print(id(a)) #1124 ...

  7. android 检測右滑的WebView

    今天产品出新花样非得要右滑....检測到右滑手势后事件不做处理放在Activity中做对应的处理即可了. import android.app.Activity; import android.con ...

  8. 防火墙设置对外开放port

    今天在部署项目时,遇到项目组其它人重整了server上的iis.结果外部訪问不了所部属的项目,通过一些渠道找到了设置方法 例如以下报错的截图: 原因是"入站ICMP规则"被重整了, ...

  9. JavaScript——BOM(浏览器对象模型),时间间隔和暂停

    BOM(浏览器对象模型):能够对浏览器的窗体进行訪问和操作 1.主要的BOM体系: window------------document-------------------------------- ...

  10. HTTP Error 500.19

    HTTP Error 500.19 - Internal Server Error The requested page cannot be accessed because the related ...