php使用promise,PHP下的异步尝试四:PHP版的Promise

PHP下的异步尝试系列

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

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

// 尝试二 (增加链式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

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实现的自动执行器等

附录参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值