PHP下的异步尝试四:PHP版的Promise
PHP下的异步尝试系列
如果你还不太了解PHP下的生成器和协程,你可以根据下面目录翻阅
- PHP下的异步尝试一:初识生成器
- PHP下的异步尝试二:初识协程
- PHP下的异步尝试三:协程的PHP版thunkify自动执行器
- PHP下的异步尝试四:PHP版的Promise
- PHP下的异步尝试五:PHP版的Promise的继续完善
Promise 实现
代码结构
│ │ autoload.php
│ │ promise1.php
│ │ promise2.php
│ │ promise3.php
│ │ promise4.php
│ │ promise5.php
│ │
│ └─classes
│ Promise1.php
│ Promise2.php
│ Promise3.php
│ Promise4.php
│ Promise5.php
│ PromiseState.php
尝试一 (Promise基础)
classess/PromiseState.php
final class PromiseState
{
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
}
classess/Promise1.php
// 尝试一
class Promise1
{
private $value;
private $reason;
private $state;
public function __construct(\Closure $func = null)
{
$this->state = PromiseState::PENDING;
$func([$this, 'resolve'], [$this, 'reject']);
}
/**
* 执行回调方法里的resolve绑定的方法
* @param null $value
*/
public function resolve($value = null)
{
// 回调执行resolve传参的值,赋值给result
$this->value = $value;
if ($this->state == PromiseState::PENDING) {
$this->state = PromiseState::FULFILLED;
}
}
public function reject($reason = null)
{
// 回调执行resolve传参的值,赋值给result
$this->reason = $reason;
if ($this->state == PromiseState::PENDING) {
$this->state = PromiseState::REJECTED;
}
}
public function getState()
{
return $this->state;
}
public function getValue()
{
return $this->value;
}
public function getReason()
{
return $this->reason;
}
}
promise1.php
require "autoload.php";
$promise = new Promise1(function($resolve, $reject) {
$resolve("打印我");
});
var_dump($promise->getState());
var_dump($promise->getValue());
结果:
string(9) "fulfilled"
string(9) "打印我"
结论或问题:
我们在这里建构了最基础的Promise模型
尝试二 (增加链式then)
classess/Promise2.php
<?php
// 尝试二 (增加链式then)
class Promise2
{
private $value;
private $reason;
private $state;
public function __construct(\Closure $func = null)
{
$this->state = PromiseState::PENDING;
$func([$this, 'resolve'], [$this, 'reject']);
}
public function then(\Closure $onFulfilled = null, \Closure $onRejected = null)
{
// 如果状态是fulfilled,直接回调执行并传参value
if ($this->state == PromiseState::FULFILLED) {
$onFulfilled($this->value);
}
// 如果状态是rejected,直接回调执行并传参reason
if ($this->state == PromiseState::REJECTED) {
$onRejected($this->reason);
}
// 返回对象自身,实现链式调用
return $this;
}
/**
* 执行回调方法里的resolve绑定的方法
* 本状态只能从pending->fulfilled
* @param null $value
*/
public function resolve($value = null)
{
if ($this->state == PromiseState::PENDING) {
$this->state = PromiseState::FULFILLED;
$this->value = $value;
}
}
/**
* 执行回调方法里的rejected绑定的方法
* 本状态只能从pending->rejected
* @param null $reason
*/
public function reject($reason = null)
{
if ($this->state == PromiseState::PENDING) {
$this->state = PromiseState::REJECTED;
$this->reason = $reason;
}
}
public function getState()
{
return $this->state;
}
public function getValue()
{
return $this->value;
}
public function getReason()
{
return $this->reason;
}
}
promise2.php
<?php
require "autoload.php";
$promise = new Promise2(function($resolve, $reject) {
$resolve("打印我");
});
$promise->then(function ($value) {
var_dump($value);
}, function ($reason) {
var_dump($reason);
})->then(function ($value) {
var_dump($value);
}, function ($reason) {
var_dump($reason);
});
结果:
string(9) "打印我"
string(9) "打印我"
结论或问题:
我们实现了链式then方法
如果我们的构造里的回调是异步执行的话,那么状态在没有变成fulfilled之前,我们then里的回调方法就永远没法执行
尝试三(真正的链式then)
classess/Promise3.php
// 解决思路:我们肯定要把then传入的回调,放到Promise构造里回调代码执行完后resolve调用后改变了state状态后再调用,所以我们必须存储到一个地方并方便后续调用
// 我们需要改造then、resolve和reject方法
class Promise3
{
private $value;
private $reason;
private $state;
private $fulfilledCallbacks = [];
private $rejectedCallbacks = [];
public function __construct(\Closure $func = null)
{
$this->state = PromiseState::PENDING;
$func([$this, 'resolve'], [$this, 'reject']);
}
public function then(\Closure $onFulfilled = null, \Closure $onRejected = null)
{
// 如果是异步回调,状态未变化之前,then的回调方法压入相应的数组方便后续调用
if ($this->state == PromiseState::PENDING) {
$this->fulfilledCallbacks[] = static function() use ($onFulfilled, $value){
$onFulfilled($this->value);
};
$this->rejectedCallbacks[] = static function() use ($onRejected, $reason){
$onRejected($this->reason);
};
}
// 如果状态是fulfilled,直接回调执行并传参value
if ($this->state == PromiseState::FULFILLED) {
$onFulfilled($this->value);
}
// 如果状态是rejected,直接回调执行并传参reason
if ($this->state == PromiseState::REJECTED) {
$onRejected($this->reason);
}
// 返回对象自身,实现链式调用
return $this;
}
/**
* 执行回调方法里的resolve绑定的方法
* 本状态只能从pending->fulfilled
* @param null $value
*/
public function resolve($value = null)
{
if ($this->state == PromiseState::PENDING) {
$this->state = PromiseState::FULFILLED;
$this->value = $value;
array_walk($this->fulfilledCallbacks, function ($callback) {
$callback();
});
}
}
/**
* 执行回调方法里的rejected绑定的方法
* 本状态只能从pending->rejected
* @param null $reason
*/
public function reject($reason = null)
{
if ($this->state == PromiseState::PENDING) {
$this->state = PromiseState::REJECTED;
$this->reason = $reason;
}
}
public function getState()
{
return $this->state;
}
public function getValue()
{
return $this->value;
}
public function getReason()
{
return $this->reason;
}
}
promise3.php
require "autoload.php";
$promise = new Promise3(function($resolve, $reject) {
$resolve("打印我");
});
$promise->then(function ($value) {
var_dump($value);
}, function ($reason) {
var_dump($reason);
})->then(function ($value) {
var_dump($value);
}, function ($reason) {
var_dump($reason);
});
结果:
string(9) "打印我"
string(9) "打印我"
结论或问题:
我们这次基本实现了真正的链式then方法
不过在Promise/A+里规范,要求then返回每次都要求是一个新的Promise对象
then方法成功执行,相当于返回一个实例一个Promise回调里执行resolve方法,resolve值为then里return的值
then方法执行失败或出错,相当于返回一个实例一个Promise回调里执行rejected方法,rejected值为then里return的值
尝试四(then返回pormise对象, 并传递上一次的结果给下一个Promise对象)
classess/Promise4.php
class Promise4
{
private $value;
private $reason;
private $state;
private $fulfilledCallbacks = [];
private $rejectedCallbacks = [];
public function __construct(\Closure $func = null)
{
$this->state = PromiseState::PENDING;
$func([$this, 'resolve'], [$this, 'reject']);
}
public function then(\Closure $onFulfilled = null, \Closure $onRejected = null)
{
$thenPromise = new Promise4(function ($reslove, $reject) use (&$thenPromise, $onFulfilled, $onRejected) {
//$this 代表的当前的Promise对象,不要混淆了
// 如果是异步回调,状态未变化之前,then的回调方法压入相应的数组方便后续调用
if ($this->state == PromiseState::PENDING) {
$this->fulfilledCallbacks[] = static function() use ($thenPromise, $onFulfilled, $reslove, $reject){
$value = $onFulfilled($this->value);
$this->resolvePromise($thenPromise, $value, $reslove, $reject);
};
$this->rejectedCallbacks[] = static function() use ($thenPromise, $onRejected, $reslove, $reject){
$reason = $onRejected($this->reason);
$this->resolvePromise($thenPromise, $reason, $reslove, $reject);
};
}
// 如果状态是fulfilled,直接回调执行并传参value
if ($this->state == PromiseState::FULFILLED) {
$value = $onFulfilled($this->value);
$this->resolvePromise($thenPromise, $value, $reslove, $reject);
}
// 如果状态是rejected,直接回调执行并传参reason
if ($this->state == PromiseState::REJECTED) {
$reason = $onRejected($this->reason);
$this->resolvePromise($thenPromise, $reason, $reslove, $reject);
}
});
// 返回对象自身,实现链式调用
return $thenPromise;
}
/**
* 解决Pormise链式then传递
* 可参考 [Promises/A+]2.3 [https://promisesaplus.com/#the-promise-resolution-procedure]
* @param $thenPromise
* @param $x $x为thenable对象
* @param $resolve
* @param $reject
*/
private function resolvePromise($thenPromise, $x, $resolve, $reject)
{
$called = false;
if ($thenPromise === $x) {
return $reject(new \Exception('循环引用'));
}
if ( is_object($x) && method_exists($x, 'then')) {
$resolveCb = function ($value) use($thenPromise, $resolve, $reject, $called) {
if ($called) return ;
$called = true;
// 成功值y有可能还是promise或者是具有then方法等,再次resolvePromise,直到成功值为基本类型或者非thenable
$this->resolvePromise($thenPromise, $value, $resolve, $reject);
};
$rejectCb = function($reason) use($thenPromise, $resolve, $reject, $called) {
if ($called) return ;
$called = true;
$reject($reason);
};
call_user_func_array([$x, 'then'], [$resolveCb, $rejectCb]);
} else {
if ($called) return ;
$called = true;
$resolve($x);
}
}
/**
* 执行回调方法里的resolve绑定的方法
* 本状态只能从pending->fulfilled
* @param null $value
*/
public function resolve($value = null)
{
if ($this->state == PromiseState::PENDING) {
$this->state = PromiseState::FULFILLED;
$this->value = $value;
array_walk($this->fulfilledCallbacks, function ($callback) {
$callback();
});
}
}
/**
* 执行回调方法里的rejected绑定的方法
* 本状态只能从pending->rejected
* @param null $reason
*/
public function reject($reason = null)
{
if ($this->state == PromiseState::PENDING) {
$this->state = PromiseState::REJECTED;
$this->reason = $reason;
}
}
public function getState()
{
return $this->state;
}
public function getValue()
{
return $this->value;
}
public function getReason()
{
return $this->reason;
}
}
promise4.php
require "autoload.php";
$promise1 = new Promise4(function($resolve, $reject) {
$resolve("打印我");
});
$promise2 = $promise1->then(function ($value) {
var_dump($value);
return "promise2";
}, function ($reason) {
var_dump($reason);
});
$promise3 = $promise2->then(function ($value) {
var_dump($value);
return new Promise4(function($resolve, $reject) {
$resolve("promise3");
});
}, function ($reason) {
var_dump($reason);
});
$promise4 = $promise3->then(function ($value) {
var_dump($value);
return "promise4";
}, function ($reason) {
var_dump($reason);
});
var_dump($promise4);
结果:
string(9) "打印我"
string(8) "promise2"
string(8) "promise3"
object(Promise4)#15 (5) {
["value":"Promise4":private]=>
string(8) "promise4"
["reason":"Promise4":private]=>
NULL
["state":"Promise4":private]=>
string(9) "fulfilled"
["fulfilledCallbacks":"Promise4":private]=>
array(0) {
}
["rejectedCallbacks":"Promise4":private]=>
array(0) {
}
}
结论或问题:
一个基本的Pormise,不过我们上面都是基于成功fulfilled状态的实现
下面我们来增加错误捕获
尝试五(错误捕获)
classess/Promise5.php
class Promise5
{
private $value;
private $reason;
private $state;
private $fulfilledCallbacks = [];
private $rejectedCallbacks = [];
public function __construct(\Closure $func = null)
{
$this->state = PromiseState::PENDING;
$func([$this, 'resolve'], [$this, 'reject']);
}
public function then(\Closure $onFulfilled = null, \Closure $onRejected = null)
{
// 此处作用是兼容then方法的以下四种参数变化,catchError就是第二种情况
// 1. then($onFulfilled, null)
// 2. then(null, $onRejected)
// 3. then(null, null)
// 4. then($onFulfilled, $onRejected)
$onFulfilled = is_callable($onFulfilled) ? $onFulfilled : function ($value) {return $value;};
$onRejected = is_callable($onRejected) ? $onRejected : function ($reason) {throw $reason;};
$thenPromise = new Promise5(function ($reslove, $reject) use (&$thenPromise, $onFulfilled, $onRejected) {
//$this 代表的当前的Promise对象,不要混淆了
// 如果是异步回调,状态未变化之前,then的回调方法压入相应的数组方便后续调用
if ($this->state == PromiseState::PENDING) {
$this->fulfilledCallbacks[] = static function() use ($thenPromise, $onFulfilled, $reslove, $reject){
try {
$value = $onFulfilled($this->value);
$this->resolvePromise($thenPromise, $value, $reslove, $reject);
} catch (\Exception $e) {
$reject($e);
}
};
$this->rejectedCallbacks[] = static function() use ($thenPromise, $onRejected, $reslove, $reject){
try {
$reason = $onRejected($this->reason);
$this->resolvePromise($thenPromise, $reason, $reslove, $reject);
} catch (\Exception $e) {
$reject($e);
}
};
}
// 如果状态是fulfilled,直接回调执行并传参value
if ($this->state == PromiseState::FULFILLED) {
try {
$value = $onFulfilled($this->value);
$this->resolvePromise($thenPromise, $value, $reslove, $reject);
} catch (\Exception $e) {
$reject($e);
}
}
// 如果状态是rejected,直接回调执行并传参reason
if ($this->state == PromiseState::REJECTED) {
try {
$reason = $onRejected($this->reason);
$this->resolvePromise($thenPromise, $reason, $reslove, $reject);
} catch (\Exception $e) {
$reject($e);
}
}
});
// 返回对象自身,实现链式调用
return $thenPromise;
}
public function catchError($onRejected)
{
return $this->then(null, $onRejected);
}
/**
* 解决Pormise链式then传递
* 可参考 [Promises/A+]2.3 [https://promisesaplus.com/#the-promise-resolution-procedure]
* @param $thenPromise
* @param $x $x为thenable对象
* @param $resolve
* @param $reject
*/
private function resolvePromise($thenPromise, $x, $resolve, $reject)
{
$called = false;
if ($thenPromise === $x) {
return $reject(new \Exception('循环引用'));
}
if ( is_object($x) && method_exists($x, 'then')) {
try {
$resolveCb = function ($value) use ($thenPromise, $resolve, $reject, $called) {
if ($called) return;
$called = true;
// 成功值y有可能还是promise或者是具有then方法等,再次resolvePromise,直到成功值为基本类型或者非thenable
$this->resolvePromise($thenPromise, $value, $resolve, $reject);
};
$rejectCb = function ($reason) use ($thenPromise, $resolve, $reject, $called) {
if ($called) return;
$called = true;
$reject($reason);
};
call_user_func_array([$x, 'then'], [$resolveCb, $rejectCb]);
} catch (\Exception $e) {
if ($called) return ;
$called = true;
$reject($e);
}
} else {
if ($called) return ;
$called = true;
$resolve($x);
}
}
/**
* 执行回调方法里的resolve绑定的方法
* 本状态只能从pending->fulfilled
* @param null $value
*/
public function resolve($value = null)
{
if ($this->state == PromiseState::PENDING) {
$this->state = PromiseState::FULFILLED;
$this->value = $value;
array_walk($this->fulfilledCallbacks, function ($callback) {
$callback(); //因为回调本身携带了作用于,所以直接调用,无法参数
});
}
}
/**
* 执行回调方法里的rejected绑定的方法
* 本状态只能从pending->rejected
* @param null $reason
*/
public function reject($reason = null)
{
if ($this->state == PromiseState::PENDING) {
$this->state = PromiseState::REJECTED;
$this->reason = $reason;
array_walk($this->rejectedCallbacks, function ($callback) {
$callback(); //因为回调本身携带了作用于,所以直接调用,无法参数
});
}
}
public function getState()
{
return $this->state;
}
public function getValue()
{
return $this->value;
}
public function getReason()
{
return $this->reason;
}
}
promise5.php
require "autoload.php";
$promise1 = new Promise5(function($resolve, $reject) {
$resolve("打印我");
});
$promise2 = $promise1->then(function ($value) {
var_dump($value);
throw new \Exception("promise2 error");
return "promise2";
}, function ($reason) {
var_dump($reason->getMessage());
return "promise3 error return";
});
//我们可以简写then方法,只传入$onFulfilled方法,然后错误会自己冒泡方式到下一个catchError或then里处理。
//$promise3 = $promise2->then(function ($value) {
// var_dump($value);
// return new Promise5(function($resolve, $reject) {
// $resolve("promise3");
// });
//})->catchError(function ($reason) {
// var_dump($reason->getMessage());
// return "promise3 error return";
//});
$promise3 = $promise2->then(function ($value) {
var_dump($value);
return new Promise5(function($resolve, $reject) {
$resolve("promise3");
});
}, function ($reason) {
var_dump($reason->getMessage());
return "promise3 error return";
});
$promise4 = $promise3->then(function ($value) {
var_dump($value);
return "promise4";
}, function ($reason) {
echo $reason->getMessage();
});
var_dump($promise4);
结果:
string(9) "打印我"
string(14) "promise2 error"
string(21) "promise3 error return"
object(Promise4)#10 (5) {
["value":"Promise4":private]=>
string(8) "promise4"
["reason":"Promise4":private]=>
NULL
["state":"Promise4":private]=>
string(9) "fulfilled"
["fulfilledCallbacks":"Promise4":private]=>
array(0) {
}
["rejectedCallbacks":"Promise4":private]=>
array(0) {
}
}
结论或问题:
这里我们基础实现了一个可以用于生产环境的Promise
后续我们会接续完善这个Promise的特有方法,比如:finally, all, race, resolve, reject等
后续再介绍用Promise实现的自动执行器等
附录参考
Promises/A+
Promises/A+ 中文
Promise 对象 - ECMAScript 6 入门 阮一峰
原文地址:https://segmentfault.com/a/1190000016564649
PHP下的异步尝试四:PHP版的Promise的更多相关文章
- PHP下的异步尝试三:协程的PHP版thunkify自动执行器
PHP下的异步尝试系列 如果你还不太了解PHP下的生成器和协程,你可以根据下面目录翻阅 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunk ...
- PHP下的异步尝试二:初识协程
PHP下的异步尝试系列 如果你还不太了解PHP下的生成器,你可以根据下面目录翻阅 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunkify ...
- PHP下的异步尝试一:初识生成器
PHP下的异步尝试系列 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunkify自动执行器 PHP下的异步尝试四:PHP版的Promise ...
- 刚刚研究了下ipv6,尝试配置内网VPS的IPv6地址
刚刚研究了下ipv6,尝试配置内网VPS的IPv6地址是3台设备,分别是客户机Windows系统.核心交换机.PPPoE拨号的路由器 第一步:在PPPoE拨号的路由器上面查看ppp0拨号的地址 ifc ...
- phpStudy模式下安装ssl证书,详细版
phpStudy模式下安装ssl证书,详细版 2017年12月16日 14:27:38 骑着蚂蚁追大象 阅读数:4232 标签: phpstudy安装ssl证书 更多 个人分类: php 版权声明 ...
- 初探.net framework 下的异步多线程
初探.net framework 下的异步多线程 目录 1.多线程的出现条件 2.Thread和ThreadPool的相关Api及用法 3.Task和Parallel的相关Api及用法 4.Async ...
- 一句代码美化你的下框之jquery.selectMM修复版(jquery.selectMM v0.9 beta 20141217)
一句代码美化你的下框之jquery.selectMM修复版(jquery.selectMM v0.9 beta 20141217) 浏览效果: http://www.beyond630.com/jqu ...
- TensorFlow笔记-02-Windows下搭建TensorFlow环境(win版非虚拟机)
TensorFlow笔记-02-Windows下搭建TensorFlow环境(win版非虚拟机) 本篇介绍的是在windows系统下,使用 Anaconda+PyCharm,不使用虚拟机,也不使用 L ...
- PHP 命令行模式实战之cli+mysql 模拟队列批量发送邮件(在Linux环境下PHP 异步执行脚本发送事件通知消息实际案例)
源码地址:https://github.com/Tinywan/PHP_Experience 测试环境配置: 环境:Windows 7系统 .PHP7.0.Apache服务器 PHP框架:ThinkP ...
随机推荐
- flask_sqlalchemy和sqlalchemy联系区别及其使用方式
### 使用SQLAlchemy去连接数据库: 1.使用SQLALchemy去连接数据库,需要使用一些配置信息,然后将他们组合成满足条件的字符串:HOSTNAME = '127.0.0.1'PORT ...
- java实现根据起点终点和日期查询去哪儿网的火车车次和火车站点信息
本文章为原创文章,转载请注明,欢迎评论和改正. 一,分析 之前所用的直接通过HTML中的元素值来爬取一些网页上的数据,但是一些比较敏感的数据,很多正规网站都是通过json数据存储,这些数据通过HTML ...
- Elasticsearch 入门 - 基本概念
NRT Elasticsearch 是一个 接近实时 的搜索平台.这意味着从你索引文档到其可以被搜索中间存在着一个轻微的延迟(通常为1秒钟). Cluster 一个或多个节点的完整数据.聚合索引和搜索 ...
- PHP开发实战权威指南-读书总结
从今年开始,断断续续学习PHP已经有4个月了.最初,认真学习PHP几天,就弄WordPress搭建了一个个人博客,这也符合技术人的实践理念. 最近,重温PHP开发实战权威指南,做点总结,整理下自己学习 ...
- 学习redis遇到的问题
1. pipeline为什么批量执行速度会变快? 答:是因为在tcp连接中减少了交互往返的时间,因为每次执行还要返回响应值,并且是一条执行完成之后才会执行下一条,但是批量执行只需要一次往返,所以节省了 ...
- Flume 读取实时更新的日志文件
http://blog.csdn.net/bright60/article/details/50728306 我用了第一种方法. 1. 日志文件每天roate一个新文件 a) 方案一 There i ...
- grpc mvn protobuf:compile 过程
grpc mvn protobuf:compile 过程 编写代码之后,直接使用 mvn protobuf:compile会报错,木有protoc.exe文件: 可以使用Terminal输入mvn命令 ...
- leetCode(32):Power of Two
Given an integer, write a function to determine if it is a power of two. 2的幂的二进制表示中,必定仅仅有一个"1&q ...
- Android SQLiteDatabase分析
Android中的数据存储使用的小巧的SQLite数据库. 为了方便java层使用SQLite,android做了大量的封装.提供了一些列的类和API.本文章就揭露这些封装背后的类图关系. 老规矩,首 ...
- BZOJ:2958 序列染色 DP
bzoj2958 序列染色 题目传送门 Description 给出一个长度为N由B.W.X三种字符组成的字符串S,你需要把每一个X染成B或W中的一个. 对于给出的K,问有多少种染色方式使得存在整数a ...