plutonium / 承诺
PHP 的 CommonJS Promises/A 轻量级实现,适用于 PocketMine-MP
Requires
- php: >=8.0
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^9.5 || ^7.5
- pocketmine/pocketmine-mp: ^4.0 || ^5.0
README
最初基于 ReactPHP Promise,但针对 PocketMine-MP 进行了适配
目录
介绍
承诺是一个库,实现了 PHP 的 CommonJS Promises/A。
它还提供了一些其他有用的承诺相关概念,例如连接多个承诺以及映射和减少承诺集合。
如果您以前从未听说过承诺,请先阅读此内容。
概念
延迟
延迟 代表一个可能尚未完成的计算或工作单元。通常(但不总是),该计算将是一个异步执行的,并在未来的某个时刻完成的操作。
承诺
虽然延迟代表计算本身,但 承诺 代表该计算的结果。因此,每个延迟都有一个承诺,它作为其实际结果的占位符。
API
延迟
延迟代表一个解析状态待定的操作。它具有独立的承诺和解析器部分。
$deferred = new Plutonium\Promise\Deferred(); $promise = $deferred->promise(); $deferred->resolve(mixed $value); $deferred->reject(\Throwable $reason);
该方法返回延迟的承诺。
resolve 和 reject 方法控制延迟的状态。
Deferred 构造函数接受一个可选的 $canceller
参数。有关更多信息,请参阅 Promise。
Deferred::promise()
$promise = $deferred->promise();
返回延迟的承诺,您可以将其提供给其他人,同时保留修改其状态的权限。
Deferred::resolve()
$deferred->resolve(mixed $value);
解析由 promise()
返回的承诺。所有消费者都会通过调用 $onFulfilled
(他们通过 $promise->then()
注册)并传递 $value
来通知。
如果 $value
本身是一个承诺,则该承诺将在解析后转换到该承诺的状态。
Deferred::reject()
$deferred->reject(\Throwable $reason);
拒绝由 promise()
返回的承诺,表示延迟的计算失败。所有消费者都会通过调用 $onRejected
(他们通过 $promise->then()
注册)并传递 $reason
来通知。
PromiseInterface
承诺接口为所有承诺实现提供通用接口。有关更多信息,请参阅 Promise。
承诺代表一个最终结果,该结果可以是满足(成功)及其关联值,或者拒绝(失败)及其关联原因。
一旦处于已实现或已拒绝状态,则承诺变为不可变。其状态或结果(或错误)都无法修改。
PromiseInterface::then()
$transformedPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null);
通过应用函数到承诺的实现或拒绝值来转换承诺的值。返回一个新的承诺以转换后的结果。
then()
方法在承诺上注册新的实现和拒绝处理程序(所有参数都是可选的)
$onFulfilled
将在承诺实现时被调用,并将结果作为第一个参数传递。$onRejected
将在承诺拒绝时被调用,并将原因作为第一个参数传递。
它返回一个新的承诺,该承诺将使用 $onFulfilled
或 $onRejected
的返回值来满足,无论哪个被调用,或者如果任一抛出异常,将拒绝。
承诺对在 then()
的同一调用中注册的处理程序做出以下保证
- 只有
$onFulfilled
或$onRejected
中的一个将被调用,决不会同时调用。 $onFulfilled
和$onRejected
从不会被调用多次。
另请参阅
PromiseInterface::catch()
$promise->catch(callable $onRejected);
为承诺注册一个拒绝处理程序。它是以下操作的快捷方式
$promise->then(null, $onRejected);
此外,您还可以为 $onRejected
的 $reason
参数进行类型提示,以捕获特定错误。
$promise ->catch(function (\RuntimeException $reason) { // Only catch \RuntimeException instances // All other types of errors will propagate automatically }) ->catch(function (\Throwable $reason) { // Catch other errors });
PromiseInterface::finally()
$newPromise = $promise->finally(callable $onFulfilledOrRejected);
允许您在承诺链中执行“清理”类型任务。
它安排在承诺实现或拒绝时调用 $onFulfilledOrRejected
,不带任何参数。
- 如果
$promise
实现,并且$onFulfilledOrRejected
返回成功,则$newPromise
将以与$promise
相同的值实现。 - 如果
$promise
实现,并且$onFulfilledOrRejected
抛出或返回一个被拒绝的承诺,则$newPromise
将拒绝,原因或被拒绝的承诺的原因。 - 如果
$promise
拒绝,并且$onFulfilledOrRejected
返回成功,则$newPromise
将以与$promise
相同的原因拒绝。 - 如果
$promise
拒绝,并且$onFulfilledOrRejected
抛出或返回一个被拒绝的承诺,则$newPromise
将拒绝,原因或被拒绝的承诺的原因。
finally()
的行为类似于同步的 finally 语句。当与 catch()
结合使用时,finally()
允许您编写类似于熟悉的同步 catch/finally 对的代码。
考虑以下同步代码
try { return doSomething(); } catch (\Throwable $e) { return handleError($e); } finally { cleanup(); }
类似异步代码(其中 doSomething()
返回一个承诺)可以编写
return doSomething() ->catch('handleError') ->finally('cleanup');
PromiseInterface::cancel()
$promise->cancel();
cancel()
方法通知承诺的创建者不再对操作的结果感兴趣。
一旦承诺已解决(无论是已实现还是已拒绝),则对承诺调用 cancel()
无效。
PromiseInterface::otherwise()
自 v3.0.0 起已弃用,请参阅
catch()
。
otherwise()
方法为承诺注册一个拒绝处理程序。
此方法仅存在是为了向后兼容和简化版本之间的升级。它是以下方法的别名
$promise->catch($onRejected);
PromiseInterface::always()
自 v3.0.0 起已弃用,请参阅
finally()
。
always()
方法允许您在承诺链中执行“清理”类型任务。
此方法仅存在是为了向后兼容和简化版本之间的升级。它是以下方法的别名
$promise->finally($onFulfilledOrRejected);
承诺
创建一个其状态由传递给 $resolver
的函数控制的承诺。
$resolver = function (callable $resolve, callable $reject) { // Do some work, possibly asynchronously, and then // resolve or reject. $resolve($awesomeResult); // or throw new Exception('Promise rejected'); // or $resolve($anotherPromise); // or $reject($nastyError); }; $canceller = function () { // Cancel/abort any running operations like network connections, streams etc. // Reject promise by throwing an exception throw new Exception('Promise cancelled'); }; $promise = new Plutonium\Promise\Promise($resolver, $canceller);
承诺构造函数接收一个解析器函数和一个可选的取消函数,这两个函数都将带有 3 个参数调用
$resolve($value)
- 主要函数,决定了返回的promise的命运。接受非promise值或另一个promise。当用非promise值调用时,用该值实现promise。当用另一个promise调用时,例如$resolve($otherPromise)
,promise的命运将与$otherPromise
的命运相同。$reject($reason)
- 拒绝promise的函数。建议直接抛出异常而不是使用$reject()
。
如果解析器或取消器抛出异常,promise将使用该抛出的异常作为拒绝原因被拒绝。
解析器函数将立即被调用,取消器函数仅在所有消费者都调用了promise的cancel()
方法后才会调用一次。
函数
用于创建和连接promise集合的有用函数。
所有在promise集合上工作的函数(如all()
、race()
等)都支持取消。这意味着,如果您在返回的promise上调用cancel()
,集合中的所有promise都将被取消。
resolve()
$promise = Plutonium\Promise\resolve(mixed $promiseOrValue);
为提供的$promiseOrValue
创建一个promise。
如果$promiseOrValue
是一个值,它将是返回的promise的解决值。
如果$promiseOrValue
是一个thenable(任何提供then()
方法的对象),则返回一个跟踪thenable状态的受信任promise。
如果$promiseOrValue
是一个promise,则直接返回。
reject()
$promise = Plutonium\Promise\reject(\Throwable $reason);
为提供的$reason
创建一个拒绝的promise。
注意,PHP 7中引入的\Throwable
接口涵盖了用户定义的\Exception
和PHP内部错误\Error
。通过强制将\Throwable
作为拒绝promise的原因,任何语言错误或用户定义的异常都可以用于拒绝promise。
all()
$promise = Plutonium\Promise\all(iterable $promisesOrValues);
返回一个promise,只有当$promisesOrValues
中的所有项目都已解决时,该promise才会解决。返回promise的解决值将是一个数组,包含$promisesOrValues
中每个项目的解决值。
race()
$promise = Plutonium\Promise\race(iterable $promisesOrValues);
开始一场竞争比赛,允许一个赢家。返回一个promise,该promise以与第一个解决的promise相同的方式解决。
如果$promisesOrValues
包含0个项目,则返回的promise将变为infinitely pending
。
any()
$promise = Plutonium\Promise\any(iterable $promisesOrValues);
返回一个promise,当$promisesOrValues
中的任何一项解决时,该promise将解决。返回promise的解决值将是触发项的解决值。
如果$promisesOrValues
中的所有项都被拒绝,则返回的promise将拒绝。拒绝值将是一个包含所有拒绝原因的Plutonium\Promise\Exception\CompositeException
。可以使用CompositeException::getThrowables()
获取拒绝原因。
如果$promisesOrValues
包含0个项目,则返回的promise还将以Plutonium\Promise\Exception\LengthException
拒绝。
示例
如何使用延迟
function getAwesomeResultPromise() { $deferred = new Plutonium\Promise\Deferred(); // Execute a Node.js-style function using the callback pattern computeAwesomeResultAsynchronously(function (\Throwable $error, $result) use ($deferred) { if ($error) { $deferred->reject($error); } else { $deferred->resolve($result); } }); // Return the promise return $deferred->promise(); } getAwesomeResultPromise() ->then( function ($value) { // Deferred resolved, do something with $value }, function (\Throwable $reason) { // Deferred rejected, do something with $reason } );
承诺转发的工作原理
一些简单的示例,以展示Promises/A前向工作的机制。当然,这些示例是人为设计的,在实际使用中,promise链通常跨越几个函数调用,甚至跨越您的应用程序架构的几个级别。
解析转发
解决的promise将解决值传递给下一个promise。第一个promise,$deferred->promise()
,将以在下面传递给$deferred->resolve()
的值解决。
then()
的每次调用都返回一个新的promise,该promise将以上一个处理程序的返回值解决。这创建了一个promise "管道"。
$deferred = new Plutonium\Promise\Deferred(); $deferred->promise() ->then(function ($x) { // $x will be the value passed to $deferred->resolve() below // and returns a *new promise* for $x + 1 return $x + 1; }) ->then(function ($x) { // $x === 2 // This handler receives the return value of the // previous handler. return $x + 1; }) ->then(function ($x) { // $x === 3 // This handler receives the return value of the // previous handler. return $x + 1; }) ->then(function ($x) { // $x === 4 // This handler receives the return value of the // previous handler. echo 'Resolve ' . $x; }); $deferred->resolve(1); // Prints "Resolve 4"
拒绝转发
拒绝的promise的行为类似,并且也与try/catch类似:当你捕获一个异常时,你必须重新抛出,以便它传播。
同样,当你处理一个拒绝的promise时,为了传播拒绝,你可以通过返回一个拒绝的promise或实际抛出(因为promise将抛出的异常转换为拒绝)来"重新抛出"它。
$deferred = new Plutonium\Promise\Deferred(); $deferred->promise() ->then(function ($x) { throw new \Exception($x + 1); }) ->catch(function (\Exception $x) { // Propagate the rejection throw $x; }) ->catch(function (\Exception $x) { // Can also propagate by returning another rejection return Plutonium\Promise\reject( new \Exception($x->getMessage() + 1) ); }) ->catch(function ($x) { echo 'Reject ' . $x->getMessage(); // 3 }); $deferred->resolve(1); // Prints "Reject 3"
混合解析和拒绝转发
就像try/catch一样,您可以选择是否传播。混合解析和拒绝仍然会以可预测的方式传递处理器的结果。
$deferred = new Plutonium\Promise\Deferred(); $deferred->promise() ->then(function ($x) { return $x + 1; }) ->then(function ($x) { throw new \Exception($x + 1); }) ->catch(function (\Exception $x) { // Handle the rejection, and don't propagate. // This is like catch without a rethrow return $x->getMessage() + 1; }) ->then(function ($x) { echo 'Mixed ' . $x; // 4 }); $deferred->resolve(1); // Prints "Mixed 4"
安装
安装此库的推荐方式是通过Composer。 您是Composer的新手吗?
一旦发布,此项目将遵循SemVer。目前,这将安装最新的开发版本。
composer require plutonium/promise
有关版本升级的详细信息,请参阅CHANGELOG。
此项目旨在在任何平台上运行,因此不需要任何PHP扩展,并支持在PHP 8+上运行。对于此项目,强烈建议使用最新支持的PHP版本。
致谢
Promise是when.js的移植,由Brian Cavalier提供。
此外,大部分文档已经从when.js的Wiki和API文档移植过来。
许可协议
在MIT许可证下发布。