moebius / 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 auto-updated.
Last update: 2024-09-07 01:48:23 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;使用解析器函数或不需要解析器函数 - 在这种情况下,Promise 必须通过调用 fulfill()
或 reject()
方法来解决。
使用解析器函数*
<?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" 的方法实现的。
由于这些方法可以根据底层 Promise 实现记录状态的方式来实现,因此此库实现了 isPending()
、isResolved()
和 isFulfilled()
方法。
许多 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
为了支持其他承诺实现,通常的做法是使用反射来检查then()
方法。
Moebius 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承诺的兼容性,我们进行了一些“诡计”。结果是,PhpHttp承诺是基于Guzzle承诺构建的,实际上是兼容的。Guzzle承诺是PhpHttp承诺的超集,所以如果安装了PhpHttp,它将通过class_alias()
替换GuzzleHttp\Promise\PromiseInterface
。
这些常量现在位于Moebius\Promise\SuperPromiseInterface
类中,这样它们在子类中可用,而不会根据PHP产生歧义。
承诺解析
在承诺解析方面,Guzzle非常特别,它会在您实际请求值时才延迟运行解析函数。这对于事件循环实现来说并不理想——因为例如,如果您的任务需要1秒才能运行,您会希望尽快开始运行该任务。当仅使用Guzzle时,这可能不太明显,但当与其他异步任务结合使用时,这会变得令人烦恼。
我们预计这不会成为问题。
Promise::wait()
Guzzle和PhpHttp实现了wait()
函数,该函数旨在启动异步作业的运行——几乎就像使用承诺树作为事件循环一样。
基本用法
通常,您将在构建承诺时传递一个解析函数。这是使用承诺最常见的方式。
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()); }); }
延迟用法
在某些情况下,您无法从承诺内部解析承诺。这样,您可以保留承诺的引用,并在返回后解析(或拒绝)它。
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::all(iterable $promises): Promise
。如果所有承诺都解析成功,则返回的承诺将以所有承诺的数组解析。如果任何承诺被拒绝,则返回的承诺也会被拒绝。 -
Promise::allSettled(iterable $promises): Promise
。一旦所有承诺都有结果,返回的承诺将以承诺的数组解析。 -
Promise::any(iterable $promises): Promise
。如果任何承诺被解析,则返回的承诺将以第一个值解析。如果所有承诺都拒绝,则返回的承诺也会被拒绝。 -
Promise::race(iterable $promises): Promise
。第一个解析或拒绝的承诺将是返回承诺的结果。
与其他承诺实现互操作
Promise::cast($promise): Promise
。任何具有类似承诺的“then”函数的对象都将转换为Moebius\Promise实例。
订阅承诺的结果
$promise->then(callable $onFulfilled=null, callable $onRejected=null): Promise
。如果承诺已经解决(解析或拒绝),则立即调用传递的函数。返回的承诺是一个新承诺,它将根据回调解析或拒绝值。
外部解析承诺
-
$promise->resolve($value)
. -
$promise->reject($reason)
.