ehough / promises
PHP 5.3 兼容的 guzzle/promises 分支
Requires
- php: >=5.3.29
- ehough/generators: ^1.0
Requires (Dev)
- phpunit/phpunit: ^4.0
This package is not auto-updated.
Last update: 2020-03-06 17:30:31 UTC
README
PHP 5.3 兼容的 guzzle/promises
分支。
为什么?
遗憾的是,60% 的 PHP 服务器仍在运行 PHP 5.4 及以下版本,但 guzzle/psr7
需要 PHP 5.5 或更高版本。此分支使 guzzle/promises
与 PHP 5.3.29 至 7.1 兼容。
如何使用此分支
使用方法与 guzzle/promises
相同,只是此库中的代码命名空间位于 Hough\Promise
而不是 GuzzleHttp\Promise
下。
Promises/A+ 实现,可迭代处理承诺链和解析,允许“无限”承诺链,同时保持堆栈大小恒定。阅读此博客文章以了解对承诺的一般介绍。
特性
- Promises/A+ 实现。
- 承诺解析和链式处理是迭代的,允许“无限”承诺链。
- 承诺具有同步的
wait
方法。 - 承诺可以被取消。
- 与任何具有
then
函数的对象一起工作。 - 使用
Hough\Promise\coroutine()
实现 C# 风格的异步/等待协程承诺。
快速入门
承诺表示异步操作最终的结果。与承诺交互的主要方式是通过其 then
方法,该方法注册回调以接收承诺的最终值或承诺无法满足的原因。
回调
通过提供可选的 $onFulfilled
后跟可选的 $onRejected
函数,使用 then
方法注册回调。
use Hough\Promise\Promise; $promise = new Promise(); $promise->then( // $onFulfilled function ($value) { echo 'The promise was fulfilled.'; }, // $onRejected function ($reason) { echo 'The promise was rejected.'; } );
解决 承诺意味着您要么使用值 解决 承诺,要么使用原因 拒绝 承诺。解决承诺将触发与承诺的 then
方法注册的回调。这些回调只触发一次,并按添加的顺序触发。
解决承诺
使用 resolve($value)
方法解决承诺。使用任何除 Hough\Promise\RejectedPromise
之外的值解决承诺将触发所有 onFulfilled 回调(使用拒绝的承诺解决承诺将拒绝承诺并触发 $onRejected
回调)。
use Hough\Promise\Promise; $promise = new Promise(); $promise ->then(function ($value) { // Return a value and don't break the chain return "Hello, " . $value; }) // This then is executed after the first then and receives the value // returned from the first then. ->then(function ($value) { echo $value; }); // Resolving the promise triggers the $onFulfilled callbacks and outputs // "Hello, reader". $promise->resolve('reader.');
承诺转发
承诺可以一个接一个地链式调用。链中的每个then都是一个新承诺。承诺的返回值是传递到链中下一个承诺的内容。在then回调中返回一个承诺将导致链中的后续承诺仅在返回的承诺被满足时才会满足。链中的下一个承诺将使用承诺的解决值来调用。
use Hough\Promise\Promise; $promise = new Promise(); $nextPromise = new Promise(); $promise ->then(function ($value) use ($nextPromise) { echo $value; return $nextPromise; }) ->then(function ($value) { echo $value; }); // Triggers the first callback and outputs "A" $promise->resolve('A'); // Triggers the second callback and outputs "B" $nextPromise->resolve('B');
承诺拒绝
当承诺被拒绝时,$onRejected
回调将使用拒绝原因被调用。
use Hough\Promise\Promise; $promise = new Promise(); $promise->then(null, function ($reason) { echo $reason; }); $promise->reject('Error!'); // Outputs "Error!"
拒绝转发
如果在$onRejected
回调中抛出异常,后续的$onRejected
回调将使用抛出的异常作为原因被调用。
use Hough\Promise\Promise; $promise = new Promise(); $promise->then(null, function ($reason) { throw new \Exception($reason); })->then(null, function ($reason) { assert($reason->getMessage() === 'Error!'); }); $promise->reject('Error!');
您还可以通过在$onFulfilled
或$onRejected
回调中返回一个Hough\Promise\RejectedPromise
来将拒绝沿着承诺链向下转发。
use Hough\Promise\Promise; use Hough\Promise\RejectedPromise; $promise = new Promise(); $promise->then(null, function ($reason) { return new RejectedPromise($reason); })->then(null, function ($reason) { assert($reason === 'Error!'); }); $promise->reject('Error!');
如果在$onRejected
回调中没有抛出异常,并且回调没有返回一个拒绝的承诺,则将使用从$onRejected
回调返回的值来调用下游的$onFulfilled
回调。
use Hough\Promise\Promise; use Hough\Promise\RejectedPromise; $promise = new Promise(); $promise ->then(null, function ($reason) { return "It's ok"; }) ->then(function ($value) { assert($value === "It's ok"); }); $promise->reject('Error!');
同步等待
您可以使用承诺的wait
方法同步强制承诺完成。在创建承诺时,您可以提供一个用于同步强制承诺完成的等待函数。当调用等待函数时,它应向承诺提供值或拒绝承诺。如果等待函数不提供值,则抛出异常。当调用承诺构造函数提供的等待函数时,将调用等待函数。
$promise = new Promise(function () use (&$promise) { $promise->resolve('foo'); }); // Calling wait will return the value of the promise. echo $promise->wait(); // outputs "foo"
如果在调用承诺的等待函数时遇到异常,承诺将使用异常被拒绝,并且将抛出异常。
$promise = new Promise(function () use (&$promise) { throw new \Exception('foo'); }); $promise->wait(); // throws the exception.
在已满足的承诺上调用wait
将不会触发等待函数。它将简单地返回之前解决的值。
$promise = new Promise(function () { die('this is not called!'); }); $promise->resolve('foo'); echo $promise->wait(); // outputs "foo"
在已拒绝的承诺上调用wait
将抛出异常。如果拒绝原因是\Exception
的实例,则抛出原因。否则,将抛出Hough\Promise\RejectionException
,可以通过调用异常的getReason
方法获取原因。
$promise = new Promise(); $promise->reject('foo'); $promise->wait();
PHP致命错误:未捕获的异常'Hough\Promise\RejectionException',消息为'The promise was rejected with value: foo'
解包承诺
在同步等待承诺时,您将承诺的状态加入当前执行状态(即,如果承诺被满足,则返回承诺的值;如果承诺被拒绝,则抛出异常)。这称为“解包”承诺。等待承诺将默认解包承诺状态。
您可以通过将false
传递给wait
函数的第一个参数来强制承诺解决,而不解包承诺的状态。
$promise = new Promise(); $promise->reject('foo'); // This will not throw an exception. It simply ensures the promise has // been resolved. $promise->wait(false);
在解包承诺时,将等待承诺的解决值,直到解包的值不再是承诺。这意味着如果您使用承诺B解决承诺A,并且解包承诺A,则等待函数返回的值将是传递给承诺B的值。
注意:当您不解包承诺时,不会返回任何值。
取消
您可以使用Promise对象的cancel()
方法取消尚未解决的Promise。在创建Promise时,您可以提供一个可选的取消函数,当该函数被调用时,将取消Promise的解析计算。
API
Promise
创建Promise对象时,您可以提供可选的$waitFn
和$cancelFn
。$waitFn
是一个不带参数的函数,用于解决Promise。$cancelFn
是一个不带参数的函数,用于取消Promise的解析计算。当调用Promise的cancel()
方法时,将调用此函数。
use Hough\Promise\Promise; $promise = new Promise( function () use (&$promise) { $promise->resolve('waited'); }, function () { // do something that will cancel the promise computation (e.g., close // a socket, cancel a database query, etc...) } ); assert('waited' === $promise->wait());
Promise有以下方法
-
then(callable $onFulfilled, callable $onRejected) : PromiseInterface
将解决和拒绝处理器附加到Promise上,并返回一个新的Promise,该Promise解决为调用处理器的返回值。
-
otherwise(callable $onRejected) : PromiseInterface
将拒绝处理器回调附加到Promise上,如果回调被调用,则返回回调的返回值的新Promise,如果Promise被解决,则返回其原始解决值。
-
wait($unwrap = true) : mixed
同步等待Promise完成。
$unwrap
控制是否返回已解决Promise的值,或者如果Promise被拒绝,则抛出异常。默认设置为true
。 -
cancel()
如果可能,尝试取消Promise。将被取消的Promise及其最接近尚未解决的父级也将被取消。任何等待解决被取消Promise的Promise也将被取消。
-
getState() : string
返回Promise的状态。以下是
pending
、fulfilled
或rejected
之一。 -
resolve($value)
使用给定的
$value
解决Promise。 -
reject($reason)
使用给定的
$reason
拒绝Promise。
已解决Promise
可以创建一个已解决的Promise来表示已经解决的Promise。
use Hough\Promise\FulfilledPromise; $promise = new FulfilledPromise('value'); // Fulfilled callbacks are immediately invoked. $promise->then(function ($value) { echo $value; });
已拒绝Promise
可以创建一个已拒绝的Promise来表示已经拒绝的Promise。
use Hough\Promise\RejectedPromise; $promise = new RejectedPromise('Error'); // Rejected callbacks are immediately invoked. $promise->then(null, function ($reason) { echo $reason; });
Promise互操作
此库与具有then
方法的foreign promises一起工作。这意味着您可以使用Guzzle promises与React promises等一起使用。当foreign promise在then方法回调内部返回时,Promise解决将递归发生。
// Create a React promise $deferred = new React\Promise\Deferred(); $reactPromise = $deferred->promise(); // Create a Guzzle promise that is fulfilled with a React promise. $guzzlePromise = new \Hough\Promise\Promise(); $guzzlePromise->then(function ($value) use ($reactPromise) { // Do something something with the value... // Return the React promise return $reactPromise; });
请注意,当转发foreign promise时,wait和cancel链式操作不再可能。您需要将第三方promise包装在Guzzle promise中,才能利用wait和cancel函数与foreign promises。
事件循环集成
为了保持堆栈大小不变,Guzzle promises使用任务队列异步解决。在同步等待Promise时,将自动运行任务队列,以确保阻塞Promise和任何转发Promise得到解决。在事件循环中使用Promise异步时,您需要在循环的每个tick上运行任务队列。如果您不运行任务队列,则Promise不会解决。
您可以使用全局任务队列实例的run()
方法运行任务队列。
// Get the global task queue $queue = \Hough\Promise\queue(); $queue->run();
例如,您可以使用周期性定时器使用Guzzle promises与React一起使用
$loop = React\EventLoop\Factory::create(); $loop->addPeriodicTimer(0, [$queue, 'run']);
待办事项:也许在每个tick上添加一个futureTick()
会更快?
实现说明
Promise解析和链式调用是迭代处理的
通过将待处理处理程序从一个所有者 shuffle 到另一个所有者,Promise 以迭代方式解析,从而允许“无限”的 then 链式调用。
<?php require 'vendor/autoload.php'; use Hough\Promise\Promise; $parent = new Promise(); $p = $parent; for ($i = 0; $i < 1000; $i++) { $p = $p->then(function ($v) { // The stack size remains constant (a good thing) echo xdebug_get_stack_depth() . ', '; return $v + 1; }); } $parent->resolve(0); var_dump($p->wait()); // int(1000)
当一个 Promise 通过非 Promise 值实现或拒绝时,Promise 将接管每个子 Promise 的处理程序,并使用递归向下传递值。
当一个 Promise 通过另一个 Promise 解析时,原始 Promise 将其所有待处理处理程序转移到新 Promise。当新 Promise 最终解析时,所有待处理处理程序都将收到转发值。
Promise 是延迟的。
一些 Promise 库使用延迟对象来表示计算,并使用 Promise 对象来表示计算的结果的传递。这种计算和传递的分离很好,因为 Promise 的消费者不能修改最终将传递的值。
能够迭代实现 Promise 解析和链式调用的一个副作用是,你需要让一个 Promise 能够访问另一个 Promise 的状态,以 shuffle 处理程序的所属权。为了在不使 Promise 的处理程序公开可变的情况下实现这一点,Promise 也可以是延迟值,使同一父类中的 Promise 能够访问和修改同一类型 Promise 的私有属性。虽然这确实允许消费者修改延迟的解析或拒绝,但这是保持堆栈大小恒定的微小代价。
$promise = new Promise(); $promise->then(function ($value) { echo $value; }); // The promise is the deferred value, so you can deliver a value to it. $promise->resolve('foo'); // prints "foo"