guzzlehttp7/promise

Guzzle promises 库

dev-master 2024-04-22 06:47 UTC

This package is not auto-updated.

Last update: 2024-09-10 07:07:20 UTC


README

Promises/A+ 实现的库,通过迭代处理 promise 链接和解析,允许“无限”的 promise 链接同时保持栈大小不变。阅读这篇博客文章了解 promises 的通用介绍。

特性

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

快速入门

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

回调

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

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

Promises 传递

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

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

Promises 拒绝

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

use GuzzleHttp\Promise\Promise;

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

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

拒绝传递

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

use GuzzleHttp\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 来将拒绝向下传递到 promise 链。

use GuzzleHttp\Promise\Promise;
use GuzzleHttp\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 GuzzleHttp\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时,您可以提供一个wait函数,用于同步地强制Promise完成。当调用wait函数时,它应该向Promise提供一个值或拒绝Promise。如果wait函数没有提供值,则会抛出异常。当Promise构造函数被调用时,提供的wait函数会在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的wait函数时遇到异常,Promise将被拒绝,并且异常将被抛出。

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

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

对一个已经解决的Promise调用wait不会触发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',消息 'Promise被拒绝,值为: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,wait函数返回的值将是传递给Promise B的值。

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

取消

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

API

Promise

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

use GuzzleHttp\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()

    尝试取消承诺(如果可能)。被取消的承诺及其父级最远的尚未解决的祖先也将被取消。等待已取消承诺解决的任何承诺也将被取消。

  • getState() : string

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

  • resolve($value)

    使用给定的 $value 履行承诺。

  • reject($reason)

    使用给定的 $reason 拒绝承诺。

已解决Promise

已履行的承诺可以创建来表示已解决的承诺。

use GuzzleHttp\Promise\FulfilledPromise;

$promise = new FulfilledPromise('value');

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

被拒绝Promise

被拒绝的承诺可以创建来表示已拒绝的承诺。

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

请注意,在转发国外承诺时,不再可能进行等待和取消链式操作。您需要将第三方承诺包装在 Guzzle 承诺中,才能利用国外承诺的等待和取消功能。

事件循环集成

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

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

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

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

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

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

实现说明

承诺解析和链式处理是迭代的

通过将挂起的处理程序从一方重新分配到另一方,承诺是迭代的解决的,允许进行“无限”的 then 链式操作。

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

use GuzzleHttp\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 版本中删除。这里提供了一个迁移表,以供您方便使用

原始函数替换方法
queueUtils::queue
taskUtils::task
promise_forCreate::promiseFor
rejection_forCreate::rejectionFor
exception_for创建异常::exceptionFor
iter_for创建::iterFor
检查Utils::检查
检查所有Utils::检查所有
展开Utils::展开
全部Utils::全部
一些Utils::一些
任何Utils::任何
解决Utils::解决
每个Each::of
每个限制Each::ofLimit
每个限制所有Each::ofLimitAll
!is_fulfilledIs::pending
is_fulfilledIs::fulfilled
is_rejectedIs::rejected
is_settledIs::settled
协程Coroutine::of

安全

如果您在此包中发现安全漏洞,请发送电子邮件至 security@tidelift.com。所有安全漏洞都将得到及时处理。请在修复方案公布之前,不要公开披露与安全相关的问题。请参阅安全策略获取更多信息。

许可证

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

企业版

作为 Tidelift 订阅的一部分提供

Guzzle 维护者以及成千上万的包维护者正在与 Tidelift 合作,为构建应用程序时使用的开源依赖项提供商业支持和维护。节省时间,降低风险,提高代码健康,同时支付您使用确切的依赖项的维护者。了解更多信息:点击这里