co / promise
PHP的一个灵活且兼容的promise实现。
Requires
- charm/fallback-logger: ^1.0
- charm/util-closuretool: ^1.0
- moebius/common: ^1.0
- psr/log: ^3.0
Requires (Dev)
- charm/testing: >=1.1
Suggests
- moebius/coroutine: Enable awaiting promises within an event loop
This package is not auto-updated.
Last update: 2024-09-15 08:11:00 UTC
README
一个纯粹的Promises/A+ promise实现,设计为灵活且经过良好测试,并促进互操作性。
Promise接口
Promise对象具有以下清晰的接口。此接口和实现尽可能遵循ECMAScript Promise规范。这意味着您可以假设从javascript中了解的相同语义将适用于这些promise。
namespace Moebius; interface PromiseInterface { /** * Schedule a callback to run when the promise is fulfilled * or rejected. * * @param callable $onFulfill Callback which will be invoked if the promise is fulfilled. * @param callable $onReject Callback which will be invoked if the promise is rejected. * @param callable $void Ignored; for compataiblity with other promise implementations. * @return PromiseInterface Returns a new promise which is resolved with the return value of $onFulfill/$onReject */ public function then(callable $onFulfill=null, callable $onReject=null, callable $void=null): PromiseInterface; /** * Is the promise still pending resolution? */ public function isPending(): bool; /** * Is the promise fulfilled? */ public function isFulfilled(): bool; /** * Is the promise rejected? */ public function isRejected(): bool; }
互操作性
对于意识到promise的PHP库,显式类型检查对象以兼容性是非常常见的。此库为此目的提供了一种类型检查函数。
类型转换
如果您需要确保提供的对象遵循Promises/A+语义,并具有可链的then()
- 您应使用Moebius\Promise::cast()
方法。
use Moebius\Promise; $promise = Promise::cast($somePromise); // Now you can safely check if the promise has been resolved
类型检查
use Moebius\Promise; if (Promise::isPromise($someObject)) { // You can treat $someObject as a promise }
// 类型转换 $promise = Promise::cast($someObject);
转换promise
来自其他实现的promise可以转换为Moebius\Promise。
<?php $promise = Moebius\Promise::cast($otherPromise);
识别类似promise的对象
由于没有规范化的PHP promise接口,因此有一个简单的方法来检查对象是否看起来像有效的promise是有用的。
<?php if (Moebius\Promise::isPromise($otherPromise)) { // promise has a valid `then()` method }
创建promise
通常可以通过两种方式创建promise;使用解析函数或不使用解析函数 - 在这种情况下,必须通过调用fulfill()
或reject()
方法来解析promise。
使用解析函数*
<?php $promise = new Promise(function(callable $fulfill, callable $reject) { $fulfill("Hello World"); }); $promise->then(function($value) { echo $value."\n"; });
不使用解析函数
<?php $promise = new Promise(); $promise->then(function($value) { echo $value."\n"; }); $promise->resolve("Hello World");
互操作性说明
接口专注于promise的解析;它不关心promise是如何创建的 - 接口是为接受promise的函数而设计的。
大多数promise实现都有一个广泛的API,但promise只需要公开一个then()
函数就可以被90%的库使用。
确定promise是否已解析或拒绝的方法也非常有用 - 这通常是通过返回字符串"pending"、"resolved"、"rejected"或"fulfilled"的方法实现的。
此库实现了isPending()
、isResolved()
和isFulfilled()
方法,因为这些方法可以在不依赖底层promise实现如何记录状态的情况下实现。
许多promise实现具有otherwise()
、finally()
和类似的方法 - 虽然可能很方便,但它们减少了互操作性,因为相同的特性可以用多种方式实现,并且具有不同的名称。
与各种库广泛兼容的promise实现。
模仿JavaScript中的Promises/A+规范。
如果您创建一个Moebius\Promise
实例,它可以直接用作
guzzlehttp/promises
的替代品,通过实现GuzzleHttp\Promise\PromiseInterface
react/promise
的替代品,通过实现React\Promise\PromiseInterface
- 'php-http/promise'的替代品,通过实现
Http\Promise\Promise
amphp/amp
的替代品,通过实现Amp\Promise
Moebius\Promise
对象有两个主要用途:作为标准的Promise
对象,以及使用React术语作为Deferred
对象。
基本Promise用法
这是使用promise最常见的方式
use Moebius\Promise; function some_future_result() { return new Promise(function($fulfill, $reject) { /** * Either fulfill the promise directly here, by calling * the provided $fulfill(VALUE) and $reject(REASON) callbacks * immediately, or make sure that one of these are called at * a later time. */ }); }
"Deferred"用法
在React和一些其他库中,还有一种额外的promise类型称为"deferred" promise。Moebius结合了这两种用法
use Moebius\Promise; function some_future_result() { $result = new Promise(); /** * Make sure that the promise is resolved now, by calling * `$result->resolve(VALUE)` or `$result->reject(REASON)` * here, or make sure that one of them will be called in * the future. */ return $result; }
自己支持其他promise
为了支持其他Promise实现,惯例是使用反射来检查then()
方法。
Moebius Promise提供了两种方式来支持其他Promise并确保一致的用法
转换
use Moebius\Promise; function accepting_a_promise(object $thenable) { /** * @throws InvalidArgumentException if the object is not a promise */ $promise = Moebius\Promise::cast($thenable); }
实现细节
为了保持Guzzle和PhpHttp promises的兼容性,我们进行了一些“诡计”。结果是,PhpHttp-promises是基于Guzzle promises模型的,并且实际上是可以兼容的。Guzzle promises是PhpHttp promises的超集,所以如果安装了PhpHttp,它将通过class_alias()
被替换为GuzzleHttp\Promise\PromiseInterface
。
这些常量现在位于Moebius\Promise\SuperPromiseInterface
类中,这样它们在子类中可用,同时根据PHP不会产生歧义。
Promise解析
Guzzle在Promise解析方面相当特别,它将延迟执行解析函数,直到你实际请求值。这对于事件循环实现来说不是最佳选择——因为例如,如果你的任务需要1秒钟来运行,你希望尽可能早地开始运行该任务。当仅使用Guzzle时,这并不明显,但当与其他异步任务结合使用时,就会变得令人烦恼。
我们不认为这会成为一个问题。
Promise::wait()
Guzzle和PhpHttp实现了wait()
函数,该函数旨在启动异步作业的执行——几乎就像使用promise树作为事件循环一样。
基本用法
通常,您将在构造Promise时传入一个解析函数。这种方式是使用Promise最常见的方式。
use Moebius\Promise; function some_function() { return new Promise(function($resolve, $reject) { // This function is immediately run when constructing the promise $resolve("Some value"); // or $reject(new Exception()); }); }
延迟用法
在某些情况下,您无法在Promise内部解析Promise。这种方式,您可以保持Promise的引用,并在返回它之后解析(或拒绝)它。
use Moebius\Promise; $promise = new Promise(); // send the promise off to some function some_function($promise); // resolve it at any later time $promise->resolve("Some value"); // or $promise->reject(new Exception())
注意:虽然可以多次调用解析/拒绝方法,但只有第一个调用会产生效果。
API文档
高效处理多个Promise
-
Promise::all(iterable $promises): Promise
。如果所有Promise都解析了,返回的Promise将以所有Promise的数组形式解析。如果任何一个Promise被拒绝,返回的Promise也会被拒绝。 -
Promise::allSettled(iterable $promises): Promise
。一旦所有Promise都有一个结果,返回的Promise将解析为一个Promise数组。 -
Promise::any(iterable $promises): Promise
。如果任何一个Promise被解析,返回的Promise将以第一个值解析。如果所有Promise都被拒绝,返回的Promise也会被拒绝。 -
Promise::race(iterable $promises): Promise
。第一个解析或拒绝的Promise将是返回Promise的结果。
与其他Promise实现的互操作性
Promise::cast($promise): Promise
。任何具有类似Promise的'then'函数的对象都被转换为Moebius\Promise实例。
订阅Promise的结果
$promise->then(callable $onFulfilled=null, callable $onRejected=null): Promise
。如果Promise已经解析(解析或拒绝),传递的函数将立即被调用。返回的Promise是一个新的Promise,它将使用回调函数的值解析或拒绝。
外部解析Promise
-
$promise->resolve($value)
. -
$promise->reject($reason)
.