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实现的自动执行器等
附录参考
相关推荐
zyyjay 2020-11-09
xuebingnan 2020-11-05
samtrue 2020-11-22
stefan0 2020-11-22
yifangs 2020-10-13
songshijiazuaa 2020-09-24
hebiwtc 2020-09-18
天步 2020-09-17
83911535 2020-11-13
whatsyourname 2020-11-13
zhouyuqi 2020-11-10
Noneyes 2020-11-10
mathchao 2020-10-28
王志龙 2020-10-28
wwwsurfphpseocom 2020-10-28
diskingchuan 2020-10-23
savorTheFlavor 2020-10-23