co/promise

PHP的一个灵活且兼容的promise实现。

1.0.103 2022-06-24 11:25 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).