hashhesh/promises

Guzzle promises 库

dev-master / 1.5.x-dev 2023-01-13 08:09 UTC

This package is not auto-updated.

Last update: 2024-09-21 14:35:58 UTC


README

Promises/A+ 实现的库,支持迭代处理 promise 连接和解决,从而允许“无限”的 promise 连接,同时保持堆栈大小不变。阅读 此博客文章 了解对 promise 的一般介绍。

注意

此包与原始包相同,唯一不同的是命名空间。如果你正在开发的项目使用旧的 psr7 版本或导致冲突,或者你无法升级核心应用程序的包版本,则使用此包。此包版本为 1.5.2

特性

  • Promises/A+ 实现。
  • Promise 解决和连接以迭代方式处理,允许“无限”的 promise 连接。
  • Promises 有一个同步的 wait 方法。
  • Promises 可以取消。
  • 与任何具有 then 函数的对象一起工作。
  • 使用 GuzzleHttp\Promise\Coroutine::of() 提供的 C# 风格的异步/等待协程 promises。

快速入门

Promise 代表异步操作最终结果。与 Promise 交互的主要方式是通过其 then 方法,该方法注册回调以接收 promise 的最终值或 promise 不能实现的原因。

回调

通过提供可选的 $onFulfilled 后跟可选的 $onRejected 函数来使用 then 方法注册回调。

use HeGuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise->then(
    // $onFulfilled
    function ($value) {
        echo 'The promise was fulfilled.';
    },
    // $onRejected
    function ($reason) {
        echo 'The promise was rejected.';
    }
);

解决 promise 意味着你要么用 value 解决 promise,要么用 reason 拒绝 promise。解决 promise 触发与 promise 的 then 方法注册的回调。这些回调只触发一次,并且按照它们添加的顺序触发。

解决 Promise

使用 resolve($value) 方法解决 promise。用除 GuzzleHttp\Promise\RejectedPromise 之外任何值解决 promise 都将触发所有 onFulfilled 回调(用拒绝的 promise 解决 promise 将拒绝 promise 并触发 $onRejected 回调)。

use HeGuzzleHttp\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.');

Promise 转发

Promises 可以一个接一个地连接。链中的每个 then 都是新的 promise。promise 的返回值将转发到链中的下一个 promise。在 then 回调中返回 promise 将导致链中的后续 promise 仅在返回的 promise 被解决时才解决。链中的下一个 promise 将使用 promise 的解决值调用。

use HeGuzzleHttp\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');

Promise 拒绝

当一个 promise 被拒绝时,将使用拒绝原因调用 $onRejected 回调。

use HeGuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise->then(null, function ($reason) {
    echo $reason;
});

$promise->reject('Error!');
// Outputs "Error!"

拒绝转发

如果 $onRejected 回调中抛出异常,则将使用抛出的异常作为原因调用后续的 $onRejected 回调。

use HeGuzzleHttp\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 回调中返回 GuzzleHttp\Promise\RejectedPromise 来向下转发拒绝。

use HeGuzzleHttp\Promise\Promise;
use HeGuzzleHttp\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 回调中没有抛出异常,且回调没有返回拒绝的 promise,则将使用从 $onRejected 回调返回的值调用下游的 $onFulfilled 回调。

use HeGuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise
    ->then(null, function ($reason) {
        return "It's ok";
    })
    ->then(function ($value) {
        assert($value === "It's ok");
    });

$promise->reject('Error!');

同步等待

您可以使用Promise的wait方法来同步强制Promise完成。创建Promise时,您可以提供一个用于同步强制Promise完成的等待函数。当调用等待函数时,它应该向Promise传递一个值或拒绝Promise。如果等待函数没有传递值,则会抛出异常。提供给Promise构造函数的等待函数在调用Promise的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的等待函数时遇到异常,Promise会因异常而拒绝,并且异常会被抛出。

$promise = new Promise(function () use (&$promise) {
    throw new Exception('foo');
});

$promise->wait(); // throws the exception.

对已解决的Promise调用wait不会触发等待函数。它将简单地返回之前解决的值。

$promise = new Promise(function () { die('this is not called!'); });
$promise->resolve('foo');
echo $promise->wait(); // outputs "foo"

对已拒绝的Promise调用wait将抛出异常。如果拒绝原因是\Exception的实例,则抛出原因。否则,将抛出GuzzleHttp\Promise\RejectionException,并且可以通过调用异常的getReason方法来获取原因。

$promise = new Promise();
$promise->reject('foo');
$promise->wait();

PHP致命错误:未捕获的异常 'GuzzleHttp\Promise\RejectionException',消息为'The promise was rejected with value: foo'

解包Promise

在同步等待Promise时,您会将Promise的状态合并到当前执行状态中(即,如果Promise已解决则返回Promise的值,如果Promise被拒绝则抛出异常)。这被称为“解包”Promise。等待Promise默认会解包Promise状态。

您可以通过将false传递给wait函数的第一个参数来强制Promise解决而不解包Promise的状态。

$promise = new Promise();
$promise->reject('foo');
// This will not throw an exception. It simply ensures the promise has
// been resolved.
$promise->wait(false);

在解包Promise时,Promise的解决值将被等待,直到解包的值不再是Promise。这意味着如果您用Promise B解决Promise A并解包Promise A,等待函数返回的值将是传递给Promise B的值。

注意:当您不解包Promise时,不会返回任何值。

取消

您可以使用Promise的cancel方法取消尚未解决的Promise。在创建Promise时,您可以提供一个可选的取消函数,当调用此函数时,将取消计算Promise解决值的行为。

API

Promise

在创建Promise对象时,您可以提供可选的$waitFn$cancelFn$waitFn是一个不带参数调用的函数,它应该解决Promise。$cancelFn是一个不带参数的函数,它应该取消Promise的计算。当调用Promise的cancel方法时,会调用此函数。

use HeGuzzleHttp\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解决为回调的返回值;如果Promise被解决,则返回其原始解决值。

  • wait($unwrap = true) : mixed

    同步等待Promise完成。

    $unwrap控制是否返回已解决Promise的值或如果Promise被拒绝则抛出异常。默认设置为true

  • cancel()

    如果可能,尝试取消Promise。被取消的Promise及其最接近尚未解决的父级也将被取消。任何等待已取消Promise解决的Promise也将被取消。

  • getState() : string

    返回Promise的状态。可以是pendingfulfilledrejected之一。

  • resolve($value)

    使用给定的$value解决Promise。

  • reject($reason)

    使用给定的$reason拒绝Promise。

已解决Promise

可以创建一个已实现的承诺来表示已经实现的承诺。

use HeGuzzleHttp\Promise\FulfilledPromise;

$promise = new FulfilledPromise('value');

// Fulfilled callbacks are immediately invoked.
$promise->then(function ($value) {
    echo $value;
});

被拒绝Promise

可以创建一个已拒绝的承诺来表示已经拒绝的承诺。

use HeGuzzleHttp\Promise\RejectedPromise;

$promise = new RejectedPromise('Error');

// Rejected callbacks are immediately invoked.
$promise->then(null, function ($reason) {
    echo $reason;
});

承诺互操作性

此库与具有 then 方法的国外承诺一起工作。这意味着您可以使用 Guzzle 承诺与 React 承诺 等。例如,当国外承诺在 then 方法回调内部返回时,承诺解析将递归发生。

// 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 HeGuzzleHttp\Promise\Promise();
$guzzlePromise->then(function ($value) use ($reactPromise) {
    // Do something something with the value...
    // Return the React promise
    return $reactPromise;
});

请注意,在转发国外承诺时,等待和取消链式调用不再可能。您需要使用 Guzzle 承诺包装第三方承诺,以便利用国外承诺的等待和取消功能。

事件循环集成

为了保持堆栈大小恒定,Guzzle 承诺使用任务队列异步解析。当同步等待承诺时,将自动运行任务队列以确保阻塞承诺和任何转发的承诺得到解决。当在事件循环中使用承诺异步时,您需要在循环的每个tick上运行任务队列。如果您不运行任务队列,那么承诺将不会得到解决。

您可以使用全局任务队列实例的 run() 方法来运行任务队列。

// Get the global task queue
$queue = HeGuzzleHttp\Promise\Utils::queue();
$queue->run();

例如,您可以使用周期性定时器与 React 一起使用 Guzzle 承诺

$loop = React\EventLoop\Factory::create();
$loop->addPeriodicTimer(0, [$queue, 'run']);

待办事项:也许在每个tick上添加一个 futureTick() 会更快?

实现说明

承诺解析和链式调用是迭代的

通过将挂起的处理程序从一方转移到另一方,承诺是迭代解决的,允许进行“无限”的 then 链式调用。

<?php
require 'vendor/autoload.php';

use HeGuzzleHttp\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 = 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"

从函数API升级

静态API最初在1.4.0版本中引入,以减轻全局和本地包之间的函数冲突问题。函数API将在2.0.0版本中删除。这里提供了一个迁移表,供您方便使用

安全

如果您在此包中发现安全漏洞,请发送电子邮件至 [email protected]。所有安全漏洞都将得到及时解决。请在宣布修复方案之前,不要公开安全相关问题。请参阅 安全策略 了解更多信息。

许可证

Guzzle 在 MIT 许可证(MIT)下提供。有关更多信息,请参阅 许可证文件

对于企业

作为 Tidelift 订阅的一部分提供

Gouzzle和其他数千个软件包的维护者正在与Tidelift合作,为您提供构建应用程序所需的开放源代码依赖项的商业支持和维护。节省时间、降低风险、提高代码质量,同时支付您使用的确切依赖项的维护者费用。 了解更多。