ajayvohra2005/hack-promises

Hack语言中的Promises/A+实现

v1.0.0 2021-10-16 14:55 UTC

This package is auto-updated.

Last update: 2024-09-05 07:52:57 UTC


README

本项目提供Promises/A+Hack中的实现。

概述

关键特性

  • Promise具有同步的waitcancel方法。
  • 与任何实现了HackPromises\ThenableInterface的对象一起工作
  • 支持Hack HH\KeyedIterator和Hack 生成器

要求

HHVM 4.128及以上。

安装

  • 使用Git克隆此仓库

  • 安装composer

  • 在此仓库的根目录下运行以下命令

      composer install
    

要使用此包,

    composer require ajayvohra2005/hack-promises

运行测试

安装后,在仓库根目录下运行以下命令

    ./vendor/bin/hacktest tests/

许可协议

本项目在MIT许可证(MIT)下提供。有关更多信息,请参阅本项目的LICENSE文件。

教程

Promise表示异步操作的结果及其相关副作用。

解决Promise

解决Promise意味着Promise要么以value实现,要么以reason拒绝。解决Promise将触发与Promise的then方法注册的回调。这些回调只触发一次,并且按照它们被添加的顺序触发。当回调被触发时,它被添加到全局任务队列中。当全局任务队列被run时,队列上的任务将被移除并按它们被添加到队列的顺序执行。

全局任务队列

全局任务队列可以根据需要或事件循环运行。默认情况下,全局任务队列在程序关闭之前隐式地运行一次。全局任务队列可以像下面这样显式地运行

  HackPromises\TaskQueue::globalTaskQueue()->run();

回调

通过调用Promise的then方法并提供一个可选的$onFulfilled$onRejected函数来注册回调,其中这些函数必须是HackPromises\ThenCallback类型的。

当你通过调用Promise的then方法注册回调时,它总是返回一个新的Promise。这可以用来创建Promise链。链中的下一个Promise使用链中前一个Promise的解决值调用。只有当链中的前一个Promise被解决时,链中的Promise才会被解决。

快速示例

以下是一个快速入门示例,说明了迄今为止讨论的概念

use namespace HackPromises;
use type HackPromises\Promise;

<<__EntryPoint>>
function quick_start_example(): void {
  require_once(__DIR__.'/../vendor/autoload.hack');
  \Facebook\AutoloadMap\initialize();

  $promise = new Promise();

  $promise
    ->then((mixed $value): mixed ==> {
        // Return a value and don't break the chain
        return "Hello, " . ($value as string);
    })
    // This then is executed after the first then and receives the value
    // returned from the first then.
    ->then((mixed $value): void ==> {
        $msg = $value as string;
        echo "{$msg}\n";
    });

  // Resolving the promise triggers the callback, and outputs 'Hello, reader.'.
  // The callbacks are executed on the global task queue, prior to program shutdown
  $promise->resolve('reader.');
  
}

Promise拒绝

当Promise被拒绝时,$onRejected回调将使用拒绝原因调用,如下面的示例所示

use type HackPromises\Promise;

<<__EntryPoint>>
function promise_rejection_example(): void {
  require_once(__DIR__.'/../vendor/autoload.hack');
  \Facebook\AutoloadMap\initialize();

    $promise = new Promise();
    $promise
        ->then(null, (mixed $reason): void ==> {
            $msg = $reason as string;
            echo "{$msg}\n";
        });

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

拒绝转发

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

use type HackPromises\{Promise, RejectedPromise};

<<__EntryPoint>>
function rejection_forwarding_example(): void {
  require_once(__DIR__.'/../vendor/autoload.hack');
  \Facebook\AutoloadMap\initialize();

    $promise = new Promise();
    $promise
        ->then(null, (mixed $reason): mixed ==> {
            return new RejectedPromise($reason);
        })
        ->then(null, (mixed $reason): void ==> {
        });

    // Outputs nothing
    $promise->reject('No Error!');
}

如果$onRejected回调中没有抛出异常,并且回调没有返回一个拒绝的Promise,那么将使用从$onRejected回调返回的值调用下游的$onFulfilled回调,如下面的示例所示

use namespace HackPromises as P;

<<__EntryPoint>>
function ignore_rejection_example(): void 
{
  require_once(__DIR__.'/../vendor/autoload.hack');
  \Facebook\AutoloadMap\initialize();
  
  $promise = new P\Promise();
  $promise ->then(null, (mixed $reason): mixed ==> {
        return "It's ok";
    })->then( (mixed $value): void ==> {
        echo $value as string;
    });

  // Outputs 'It's ok'
  $promise->reject('Error!');
}

同步等待

您可以通过在承诺上调用 wait 方法来同步解决承诺。

调用承诺的 wait 方法会调用承诺提供的 wait 函数,并隐式运行全局任务队列。以下是一个展示 wait 方法使用的示例:

use namespace HackPromises;

use type HackPromises\Promise;
use type HackPromises\ResolveCallback;

<<__EntryPoint>>
function promise_wait_function_example(): void {
  require_once(__DIR__.'/../vendor/autoload.hack');
  \Facebook\AutoloadMap\initialize();

  $wait_fn = (ResolveCallback $cb) ==> {
    $cb('reader.');
  };

  $promise = new Promise($wait_fn);

  $promise
    ->then((mixed $value): mixed ==> {
        // Return a value and don't break the chain
        return "Hello, " . ($value as string);
    })
    // This then is executed after the first then and receives the value
    // returned from the first then.
    ->then((mixed $value): void ==> {
        $msg = $value as string;
        echo "{$msg}\n";
    });

  // Calling wait  resolves promise synchronously and outputs 'Hello, reader.'.
  $promise->wait();
}

如果在调用承诺的 wait 函数时遇到异常,承诺将被拒绝,并抛出异常。调用已解决的承诺的 wait 方法不会触发 wait 函数。它将直接返回之前解决的价值。

对已拒绝的承诺调用 wait 方法将抛出异常。如果拒绝原因是一个 \Exception 的实例,则抛出 reason。否则,将抛出 HackPromises\RejectionException,可以通过调用异常的 getReason 方法来获取 reason

拆包承诺

当您不带参数或使用参数 true 调用 wait 方法时,它会拆包承诺。拆包承诺如果承诺是 fulfilled,则返回承诺的值,如果承诺是 rejected,则抛出异常。您可以通过将 false 传递给承诺的 wait 方法来强制承诺解决但 拆包,如下面的示例所示:

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

在拆包承诺时,承诺的解决值将等待,直到拆包的值不再是承诺。这意味着如果您用承诺 B 解决承诺 A 并拆包承诺 A,wait 函数返回的将是解决承诺 B 的结果,如下面的示例所示:

use namespace HackPromises as P;

<<__EntryPoint>>
function unwrapping_example(): void 
{
  require_once(__DIR__.'/../vendor/autoload.hack');
  \Facebook\AutoloadMap\initialize();
  
  $b = new P\Promise();
  $a = new P\Promise( (P\ResolveCallback $cb): void ==> { $cb($b);});
  $b->resolve('foo');
  $result = $a->wait() as string;

  // Outputs 'foo'
  echo $result;
}

同步取消

您可以使用承诺的 cancel 方法同步取消尚未解决的承诺。

API 类

Promise

对于基本案例,您可以使用 Promise 类创建承诺。

如果您想异步解决承诺并调用已注册的 then 回调,您可以在构造函数中不传递任何参数来创建 Promise

如果您想能够同步解决或取消 Promise,请将 waitcancel 函数传递给构造函数。

等待函数

等待函数必须是 HackPromises\WaitFunction 类型。提供的给 Promise 构造函数的等待函数会在调用 Promisewait 方法时被调用。等待函数必须通过调用作为参数传递给它的 HackPromises\ResolveCallback 函数来解决承诺,或者抛出异常。

取消函数

在创建 Promise 时,您可以选择提供一个类型为 HackPromises\CancelFunction 的可选 cancel 函数,该函数在调用承诺的 cancel 方法时被调用。取消函数可以可选地通过调用作为参数传递给它的 HashPromises\RejectCallback 函数来拒绝承诺。

FulfilledPromise

FulfilledPromise 类对象在构造时解决。

RejectedPromise

RejectedPromise 类对象在构造时拒绝。

IteratorPromise

IteratorPromise 类创建一个迭代器承诺,该迭代器遍历承诺或值的迭代器,并在过程中触发回调函数。使用 Create::iterForvec<mixed>dict<arraykey, mixed> 创建一个迭代器。

GeneratorPromise

GeneratorPromise 类包装一个产生值或承诺的生成器。

Create

Create 类提供辅助方法。

致谢

本项目受Guzzle Promises启发。然而,由于Hack语言和最佳实践的要求,它在API和实现方面存在显著差异。