plutonium/承诺

PHP 的 CommonJS Promises/A 轻量级实现,适用于 PocketMine-MP

3.1.3 2023-08-08 08:25 UTC

This package is auto-updated.

Last update: 2024-09-08 10:40:42 UTC


README

CI status

最初基于 ReactPHP Promise,但针对 PocketMine-MP 进行了适配

目录

  1. 介绍
  2. 概念
  3. API
  4. 示例
  5. 安装
  6. 致谢
  7. 许可协议

介绍

承诺是一个库,实现了 PHP 的 CommonJS Promises/A

它还提供了一些其他有用的承诺相关概念,例如连接多个承诺以及映射和减少承诺集合。

如果您以前从未听说过承诺,请先阅读此内容

概念

延迟

延迟 代表一个可能尚未完成的计算或工作单元。通常(但不总是),该计算将是一个异步执行的,并在未来的某个时刻完成的操作。

承诺

虽然延迟代表计算本身,但 承诺 代表该计算的结果。因此,每个延迟都有一个承诺,它作为其实际结果的占位符。

API

延迟

延迟代表一个解析状态待定的操作。它具有独立的承诺和解析器部分。

$deferred = new Plutonium\Promise\Deferred();

$promise = $deferred->promise();

$deferred->resolve(mixed $value);
$deferred->reject(\Throwable $reason);

该方法返回延迟的承诺。

resolve 和 reject 方法控制延迟的状态。

Deferred 构造函数接受一个可选的 $canceller 参数。有关更多信息,请参阅 Promise

Deferred::promise()

$promise = $deferred->promise();

返回延迟的承诺,您可以将其提供给其他人,同时保留修改其状态的权限。

Deferred::resolve()

$deferred->resolve(mixed $value);

解析由 promise() 返回的承诺。所有消费者都会通过调用 $onFulfilled(他们通过 $promise->then() 注册)并传递 $value 来通知。

如果 $value 本身是一个承诺,则该承诺将在解析后转换到该承诺的状态。

Deferred::reject()

$deferred->reject(\Throwable $reason);

拒绝由 promise() 返回的承诺,表示延迟的计算失败。所有消费者都会通过调用 $onRejected(他们通过 $promise->then() 注册)并传递 $reason 来通知。

PromiseInterface

承诺接口为所有承诺实现提供通用接口。有关更多信息,请参阅 Promise

承诺代表一个最终结果,该结果可以是满足(成功)及其关联值,或者拒绝(失败)及其关联原因。

一旦处于已实现或已拒绝状态,则承诺变为不可变。其状态或结果(或错误)都无法修改。

PromiseInterface::then()

$transformedPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null);

通过应用函数到承诺的实现或拒绝值来转换承诺的值。返回一个新的承诺以转换后的结果。

then() 方法在承诺上注册新的实现和拒绝处理程序(所有参数都是可选的)

  • $onFulfilled 将在承诺实现时被调用,并将结果作为第一个参数传递。
  • $onRejected 将在承诺拒绝时被调用,并将原因作为第一个参数传递。

它返回一个新的承诺,该承诺将使用 $onFulfilled$onRejected 的返回值来满足,无论哪个被调用,或者如果任一抛出异常,将拒绝。

承诺对在 then() 的同一调用中注册的处理程序做出以下保证

  1. 只有 $onFulfilled$onRejected 中的一个将被调用,决不会同时调用。
  2. $onFulfilled$onRejected 从不会被调用多次。

另请参阅

  • resolve() - 创建一个已解决的承诺
  • reject() - 创建一个被拒绝的承诺

PromiseInterface::catch()

$promise->catch(callable $onRejected);

为承诺注册一个拒绝处理程序。它是以下操作的快捷方式

$promise->then(null, $onRejected);

此外,您还可以为 $onRejected$reason 参数进行类型提示,以捕获特定错误。

$promise
    ->catch(function (\RuntimeException $reason) {
        // Only catch \RuntimeException instances
        // All other types of errors will propagate automatically
    })
    ->catch(function (\Throwable $reason) {
        // Catch other errors
    });

PromiseInterface::finally()

$newPromise = $promise->finally(callable $onFulfilledOrRejected);

允许您在承诺链中执行“清理”类型任务。

它安排在承诺实现或拒绝时调用 $onFulfilledOrRejected,不带任何参数。

  • 如果 $promise 实现,并且 $onFulfilledOrRejected 返回成功,则 $newPromise 将以与 $promise 相同的值实现。
  • 如果 $promise 实现,并且 $onFulfilledOrRejected 抛出或返回一个被拒绝的承诺,则 $newPromise 将拒绝,原因或被拒绝的承诺的原因。
  • 如果 $promise 拒绝,并且 $onFulfilledOrRejected 返回成功,则 $newPromise 将以与 $promise 相同的原因拒绝。
  • 如果 $promise 拒绝,并且 $onFulfilledOrRejected 抛出或返回一个被拒绝的承诺,则 $newPromise 将拒绝,原因或被拒绝的承诺的原因。

finally() 的行为类似于同步的 finally 语句。当与 catch() 结合使用时,finally() 允许您编写类似于熟悉的同步 catch/finally 对的代码。

考虑以下同步代码

try {
    return doSomething();
} catch (\Throwable $e) {
    return handleError($e);
} finally {
    cleanup();
}

类似异步代码(其中 doSomething() 返回一个承诺)可以编写

return doSomething()
    ->catch('handleError')
    ->finally('cleanup');

PromiseInterface::cancel()

$promise->cancel();

cancel() 方法通知承诺的创建者不再对操作的结果感兴趣。

一旦承诺已解决(无论是已实现还是已拒绝),则对承诺调用 cancel() 无效。

PromiseInterface::otherwise()

自 v3.0.0 起已弃用,请参阅 catch()

otherwise() 方法为承诺注册一个拒绝处理程序。

此方法仅存在是为了向后兼容和简化版本之间的升级。它是以下方法的别名

$promise->catch($onRejected);

PromiseInterface::always()

自 v3.0.0 起已弃用,请参阅 finally()

always() 方法允许您在承诺链中执行“清理”类型任务。

此方法仅存在是为了向后兼容和简化版本之间的升级。它是以下方法的别名

$promise->finally($onFulfilledOrRejected);

承诺

创建一个其状态由传递给 $resolver 的函数控制的承诺。

$resolver = function (callable $resolve, callable $reject) {
    // Do some work, possibly asynchronously, and then
    // resolve or reject.

    $resolve($awesomeResult);
    // or throw new Exception('Promise rejected');
    // or $resolve($anotherPromise);
    // or $reject($nastyError);
};

$canceller = function () {
    // Cancel/abort any running operations like network connections, streams etc.

    // Reject promise by throwing an exception
    throw new Exception('Promise cancelled');
};

$promise = new Plutonium\Promise\Promise($resolver, $canceller);

承诺构造函数接收一个解析器函数和一个可选的取消函数,这两个函数都将带有 3 个参数调用

  • $resolve($value) - 主要函数,决定了返回的promise的命运。接受非promise值或另一个promise。当用非promise值调用时,用该值实现promise。当用另一个promise调用时,例如 $resolve($otherPromise),promise的命运将与$otherPromise的命运相同。
  • $reject($reason) - 拒绝promise的函数。建议直接抛出异常而不是使用$reject()

如果解析器或取消器抛出异常,promise将使用该抛出的异常作为拒绝原因被拒绝。

解析器函数将立即被调用,取消器函数仅在所有消费者都调用了promise的cancel()方法后才会调用一次。

函数

用于创建和连接promise集合的有用函数。

所有在promise集合上工作的函数(如all()race()等)都支持取消。这意味着,如果您在返回的promise上调用cancel(),集合中的所有promise都将被取消。

resolve()

$promise = Plutonium\Promise\resolve(mixed $promiseOrValue);

为提供的$promiseOrValue创建一个promise。

如果$promiseOrValue是一个值,它将是返回的promise的解决值。

如果$promiseOrValue是一个thenable(任何提供then()方法的对象),则返回一个跟踪thenable状态的受信任promise。

如果$promiseOrValue是一个promise,则直接返回。

reject()

$promise = Plutonium\Promise\reject(\Throwable $reason);

为提供的$reason创建一个拒绝的promise。

注意,PHP 7中引入的\Throwable接口涵盖了用户定义的\Exception和PHP内部错误\Error。通过强制将\Throwable作为拒绝promise的原因,任何语言错误或用户定义的异常都可以用于拒绝promise。

all()

$promise = Plutonium\Promise\all(iterable $promisesOrValues);

返回一个promise,只有当$promisesOrValues中的所有项目都已解决时,该promise才会解决。返回promise的解决值将是一个数组,包含$promisesOrValues中每个项目的解决值。

race()

$promise = Plutonium\Promise\race(iterable $promisesOrValues);

开始一场竞争比赛,允许一个赢家。返回一个promise,该promise以与第一个解决的promise相同的方式解决。

如果$promisesOrValues包含0个项目,则返回的promise将变为infinitely pending

any()

$promise = Plutonium\Promise\any(iterable $promisesOrValues);

返回一个promise,当$promisesOrValues中的任何一项解决时,该promise将解决。返回promise的解决值将是触发项的解决值。

如果$promisesOrValues中的所有项都被拒绝,则返回的promise将拒绝。拒绝值将是一个包含所有拒绝原因的Plutonium\Promise\Exception\CompositeException。可以使用CompositeException::getThrowables()获取拒绝原因。

如果$promisesOrValues包含0个项目,则返回的promise还将以Plutonium\Promise\Exception\LengthException拒绝。

示例

如何使用延迟

function getAwesomeResultPromise()
{
    $deferred = new Plutonium\Promise\Deferred();

    // Execute a Node.js-style function using the callback pattern
    computeAwesomeResultAsynchronously(function (\Throwable $error, $result) use ($deferred) {
        if ($error) {
            $deferred->reject($error);
        } else {
            $deferred->resolve($result);
        }
    });

    // Return the promise
    return $deferred->promise();
}

getAwesomeResultPromise()
    ->then(
        function ($value) {
            // Deferred resolved, do something with $value
        },
        function (\Throwable $reason) {
            // Deferred rejected, do something with $reason
        }
    );

承诺转发的工作原理

一些简单的示例,以展示Promises/A前向工作的机制。当然,这些示例是人为设计的,在实际使用中,promise链通常跨越几个函数调用,甚至跨越您的应用程序架构的几个级别。

解析转发

解决的promise将解决值传递给下一个promise。第一个promise,$deferred->promise(),将以在下面传递给$deferred->resolve()的值解决。

then()的每次调用都返回一个新的promise,该promise将以上一个处理程序的返回值解决。这创建了一个promise "管道"。

$deferred = new Plutonium\Promise\Deferred();

$deferred->promise()
    ->then(function ($x) {
        // $x will be the value passed to $deferred->resolve() below
        // and returns a *new promise* for $x + 1
        return $x + 1;
    })
    ->then(function ($x) {
        // $x === 2
        // This handler receives the return value of the
        // previous handler.
        return $x + 1;
    })
    ->then(function ($x) {
        // $x === 3
        // This handler receives the return value of the
        // previous handler.
        return $x + 1;
    })
    ->then(function ($x) {
        // $x === 4
        // This handler receives the return value of the
        // previous handler.
        echo 'Resolve ' . $x;
    });

$deferred->resolve(1); // Prints "Resolve 4"

拒绝转发

拒绝的promise的行为类似,并且也与try/catch类似:当你捕获一个异常时,你必须重新抛出,以便它传播。

同样,当你处理一个拒绝的promise时,为了传播拒绝,你可以通过返回一个拒绝的promise或实际抛出(因为promise将抛出的异常转换为拒绝)来"重新抛出"它。

$deferred = new Plutonium\Promise\Deferred();

$deferred->promise()
    ->then(function ($x) {
        throw new \Exception($x + 1);
    })
    ->catch(function (\Exception $x) {
        // Propagate the rejection
        throw $x;
    })
    ->catch(function (\Exception $x) {
        // Can also propagate by returning another rejection
        return Plutonium\Promise\reject(
            new \Exception($x->getMessage() + 1)
        );
    })
    ->catch(function ($x) {
        echo 'Reject ' . $x->getMessage(); // 3
    });

$deferred->resolve(1);  // Prints "Reject 3"

混合解析和拒绝转发

就像try/catch一样,您可以选择是否传播。混合解析和拒绝仍然会以可预测的方式传递处理器的结果。

$deferred = new Plutonium\Promise\Deferred();

$deferred->promise()
    ->then(function ($x) {
        return $x + 1;
    })
    ->then(function ($x) {
        throw new \Exception($x + 1);
    })
    ->catch(function (\Exception $x) {
        // Handle the rejection, and don't propagate.
        // This is like catch without a rethrow
        return $x->getMessage() + 1;
    })
    ->then(function ($x) {
        echo 'Mixed ' . $x; // 4
    });

$deferred->resolve(1);  // Prints "Mixed 4"

安装

安装此库的推荐方式是通过Composer您是Composer的新手吗?

一旦发布,此项目将遵循SemVer。目前,这将安装最新的开发版本。

composer require plutonium/promise

有关版本升级的详细信息,请参阅CHANGELOG

此项目旨在在任何平台上运行,因此不需要任何PHP扩展,并支持在PHP 8+上运行。对于此项目,强烈建议使用最新支持的PHP版本

致谢

Promise是when.js的移植,由Brian Cavalier提供。

此外,大部分文档已经从when.js的WikiAPI文档移植过来。

许可协议

MIT许可证下发布。