guzzlehttp/promises

Guzzle promises 库

安装次数: 705,686,990

依赖项: 440

建议者: 7

安全性: 0

星星: 7,590

关注者: 38

分支: 116

开放问题: 12

2.0.3 2024-07-18 10:29 UTC

This package is auto-updated.

Last update: 2024-09-18 10:53:03 UTC


README

Promises/A+ 实现,能够迭代处理承诺链和解析,允许进行“无限”承诺链,同时保持堆栈大小恒定。阅读 这篇博客文章 了解承诺的一般介绍。

特性

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

安装

composer require guzzlehttp/promises

版本指导

快速入门

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

回调

通过提供可选的 $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.';
    }
);

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

解决承诺

使用 resolve($value) 方法实现承诺。用任何不是 GuzzleHttp\Promise\RejectedPromise 的值解决承诺将触发所有的 onFulfilled 回调(用被拒绝的承诺解决承诺将拒绝承诺并触发 $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.');

承诺转发

承诺可以被一个接一个地链式。链中的每个 then 都是一个新的承诺。承诺的返回值将被转发到链中的下一个承诺。在 then 回调中返回承诺将导致链中的后续承诺只有在返回的承诺被解决后才会被解决。链中的下一个承诺将使用承诺的解决值调用。

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');

承诺拒绝

当承诺被拒绝时,将使用拒绝原因调用 $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 来将拒绝沿着承诺链向下转发。

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 回调中没有抛出异常,并且回调没有返回被拒绝的承诺,则将使用从 $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函数时,会调用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',消息为'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,则wait函数返回的值将是传递给Promise B的值。

注意:当您不展开Promise时,不返回任何值。

取消

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

API

承诺

在创建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被解决,则返回其原始解决值。

  • wait($unwrap = true) : mixed

    同步等待Promise完成。

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

  • cancel()

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

  • getState() : 字符串

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

  • resolve($value)

    使用给定的 $value 履行 Promise。

  • reject($reason)

    使用给定的 $reason 拒绝 Promise。

已解决的承诺

已履行的 Promise 可以创建来表示已经履行的 Promise。

use GuzzleHttp\Promise\FulfilledPromise;

$promise = new FulfilledPromise('value');

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

被拒绝的承诺

已拒绝的 Promise 可以创建来表示已经拒绝的 Promise。

use GuzzleHttp\Promise\RejectedPromise;

$promise = new RejectedPromise('Error');

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

Promise 互操作性

此库与具有 then 方法的国外 Promise 一起工作。这意味着您可以使用 Guzzle Promise 与 React promises 等示例一起使用。当在 then 方法回调中返回国外 Promise 时,Promise 的解决将递归地进行。

// 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;
});

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

事件循环集成

为了保持栈大小不变,Guzzle Promise 使用任务队列异步解决。当同步等待 Promise 时,将自动运行任务队列,以确保阻塞的 Promise 和任何转发的 Promise 都得到解决。当在事件循环中使用 Promise 异步时,您需要在循环的每次滴答中运行任务队列。如果您不运行任务队列,则 Promise 不会得到解决。

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

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

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

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

实现说明

Promise 解决和链式处理是迭代进行的

通过在所有者之间重新分配挂起的处理程序,Promise 以迭代方式解决,允许进行“无限”的 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 用非 Promise 值履行或拒绝时,该 Promise 接管每个子 Promise 的处理程序,并将值传递到链中,而不使用递归。

当一个 Promise 用另一个 Promise 解决时,原始 Promise 将其所有挂起的处理程序转移到新 Promise。当新 Promise 最终解决时,所有挂起的处理程序都会接收到转发的值。

Promise 是延迟的

某些 Promise 库使用延迟对象来表示计算,并使用 Promise 对象来表示计算结果的交付。这种计算和交付的分离非常好,因为 Promise 的消费者不能修改最终将传递的值。

能够迭代地实现 Promise 解决和链式调用的一种副作用是,一个 Promise 需要能够访问另一个 Promise 的状态,以重新分配处理程序的所有权。为了在不使 Promise 的处理程序公开可变的情况下实现这一点,Promise 还表示延迟值,允许相同父类的 Promise 访问和修改相同类型的 Promise 的私有属性。虽然这确实允许值的消费者修改延迟的解决或拒绝,但这是为了保持栈大小不变而付出的微小代价。

$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订阅的一部分提供

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