piv915/promises

Guzzle promises 库

维护者

详细信息

github.com/piv915/promises

源代码

安装: 21

依赖: 1

建议者: 0

安全: 0

星星: 0

观察者: 1

分支: 116

1.0.2 2015-08-15 19:37 UTC

This package is not auto-updated.

Last update: 2024-10-02 09:51:55 UTC


README

Promises/A+ 实现可迭代处理 promise 连接和解析,允许“无限”promise 连接同时保持栈大小不变。阅读 这篇文章 了解 promises 的基本介绍。

功能

  • Promises/A+ 实现。
  • Promise 解析和连接处理是迭代的,允许“无限”promise 连接。
  • Promises 有一个同步的 wait 方法。
  • Promises 可以被取消。
  • 与任何具有 then 函数的对象一起工作。
  • 使用 GuzzleHttp\Promise\coroutine() 实现 C# 风格的异步/等待协程 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 意味着你要么用一个 满足一个 promise,要么用一个 原因 拒绝一个 promise。解析一个 promises 触发与 promises 的 then 方法注册的回调。这些回调只触发一次,并且按照它们被添加的顺序。

解析一个 promise

使用 resolve($value) 方法满足 promises。除了 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.');

Promise 转发

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

Promise 拒绝

当 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;
use GuzzleHttp\Promise\RejectedPromise;

$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->deliver('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->deliver('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,等待函数返回的值将是传递给Promise B的值。

注意:如果您不解包Promise,则不返回任何值。

取消

您可以使用Promise的cancel方法取消尚未解决的Promise。在创建Promise时,您可以提供一个可选的取消函数,当调用此函数时,将取消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解决时,它会解决或拒绝。

  • 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

可以创建一个已解决的Promise来表示已被解决的Promise。

use GuzzleHttp\Promise\FulfilledPromise;

$promise = new FulfilledPromise('value');

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

拒绝的Promise

可以创建一个已拒绝的Promise来表示已被拒绝的Promise。

use GuzzleHttp\Promise\RejectedPromise;

$promise = new RejectedPromise('Error');

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

Promise 互操作

此库与具有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\queue();
$queue->run();

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

$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->deliver('foo');
// prints "foo"