Amphp之Promises(承诺约定)

Posted Flybeta

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Amphp之Promises(承诺约定)相关的知识,希望对你有一定的参考价值。

目录

Amphp之Promises(承诺、约定)

Promise是一个对象,表示异步操作的最终结果。它共有三种状态:

  • Success: promise成功解决。
  • Failure:promise处理失败。
  • Pending:promise等待处理。

成功的解决就像在同步代码中返回一个值,而失败的promise就像抛出一个异常。

Promise是异步应用程序中并发的基本单位。在 Amp 中,它们实现了 Amp\\Promise接口。这些对象应被视为可能无法立即完成的值任务的占位符

处理异步 API 的另一种方法是使用在操作开始时传递的回调

doSomething(function ($error, $value) 
    if ($error) 
        /* ... */
     else 
        /* ... */
    
);

但是回调方法有几个缺点:

  • 传递回调并在其中执行取决于第一个操作的结果的进一步操作会很快变得混乱。
  • 需要显式回调作为函数的输入参数,并且返回值根本未使用。如果不涉及回调,就无法使用此 API。

这就是promise发挥作用的地方。它们是返回的简单占位符,允许注册一个回调(或多个回调)

doSomething()->onResolve(function ($error, $value) 
    if ($error) 
        /* ... */
     else 
        /* ... */
    
);

乍一看,这似乎并没有好多少,我们刚刚移动了回调。但实际上这启用了很多。我们现在可以编写像 Amp\\Promise\\all() 这样的辅助函数来订阅其中的几个占位符并将它们组合起来。我们不必编写任何复杂的代码来组合多个回调的结果。

但是 Promise 最重要的改进是它们允许编写协程,这完全消除了对任何回调的需要。

协程使用 PHP 的generators。每次产生(yield)一个 Promise 时,协程都会订阅该 Promise 并在该 Promise 解决后自动继续它。成功解析后,协程将使用 Generator::send()将解析值发送到生成器。如果失败,它将使用 Generator::throw()将异常抛出到生成器中。这允许编写几乎像同步代码一样的异步代码。

Amp 的 Promise 接口不符合 javascript Promise 实现中常见的“Thenables”抽象。链接 .then() 调用是在使用生成器协程的世界中避免“回调地狱”的次优方法。相反,Amp 使用如上所述的 PHP 生成器。
然而,由于 ReactPHP 是另一个广泛使用的实现,我们也接受任何 React\\Promise\\PromiseInterface,我们接受 Amp\\Promise 的实例。如果自定义实现未实现 React\\Promise\\PromiseInterface,则可以使用 Amp\\Promise\\adapt() 来调整任何具有 then 或 done 方法的对象。

Promise Consumption // 消费

interface Promise 
    public function onResolve(callable $onResolve);

在其最简单的形式中,Amp\\Promise 聚合回调,以便在最终解决后处理结果。由于协程,大多数代码不会直接与此 API 交互,让我们快速看一下 Amp\\Promise 实现中公开的一个简单 API 方法:

ParameterCallback Signature
$onResolvefunction ($error = null, $result = null)

Amp\\Promise::onResolve()接受错误优先回调。此回调负责对由 Promise 占位符表示的最终结果做出反应。例如:

<?php

$promise = someFunctionThatReturnsAPromise();
$promise->onResolve(function (Throwable $error = null, $result = null) 
    if ($error) 
        printf(
            "Something went wrong:\\n%s\\n",
            $error->getMessage()
        );
     else 
        printf(
            "Hurray! Our result is:\\n%s\\n",
            print_r($result, true)
        );
    
);

熟悉 JavaScript 代码的人普遍反映,上面的接口很快就沦为“回调地狱”,他们是对的。我们很快就会在协程部分看到如何避免这个问题。

Promise Creation // 创建

Promise 可以通过几种不同的方式创建。大多数代码将使用 Amp\\call(),它接受一个函数,如果它返回一个生成器,则将其作为协程运行。

Success and Failure

有时值是立即可用的。这可能是由于它们被缓存了,但也可能是如果接口要求返回promise以允许异步 I/O 但特定实现始终具有直接可用的结果。在这些情况下,可以使用 Amp\\Success 和 Amp\\Failure 来构造一个立即解决的 Promise。 Amp\\Success 接受分辨率值。 Amp\\Failure 接受异常作为失败原因。

Deferred

下面描述的延迟 API 是许多应用程序可能不需要的高级 API。尽可能使用 Amp\\call() 或 promise 组合器。

Amp\\Deferred是负责在未来值可用时对其进行解析的抽象。异步解析值的库会创建一个 Amp\\Deferred 并使用它向 API 使用者返回一个 Amp\\Promise。一旦异步库确定该值已准备就绪,它就会使用链接的 Promisor 上的方法解析 API 使用者持有的 Promise。

final class Deferred

    public function promise(): Promise;
    public function resolve($result = null);
    public function fail(Throwable $error);

promise()

返回对应的 Promise 实例。 Deferred 和 Promise 是分开的,所以 promise 的消费者无法实现。您应该始终将 $deferred->promise() 返回给 API 使用者。如果你传递 Deferred 对象,你可能做错了什么。

resolve()

使用第一个参数作为值来解析promise,否则为 null。如果通过了 Amp\\Promise,则解析将等到通过的 Promise 被解析。调用所有已注册的 Promise::onResolve() 回调。

fail()

使诺言失败。使用传递的 Throwable 作为 $error 参数调用所有已注册的 Promise::onResolve() 回调。
下面是一个简单的异步值生产者 asyncMultiply() 示例,它创建了一个 deferred 并将相关的 Promise 返回给它的 API 消费者。

<?php // Example async producer using promisor

use Amp\\Loop;

function asyncMultiply($x, $y)

    // Create a new promisor
    $deferred = new Amp\\Deferred;

    // Resolve the async result one second from now
    Loop::delay($msDelay = 1000, function () use ($deferred, $x, $y) 
        $deferred->resolve($x * $y);
    );

    return $deferred->promise();


$promise = asyncMultiply(6, 7);
$result = Amp\\Promise\\wait($promise);

var_dump($result); // int(42)

以上是关于Amphp之Promises(承诺约定)的主要内容,如果未能解决你的问题,请参考以下文章

如何动态地向promises链添加新的承诺

Node.js:何时使用 Promises 与 Callbacks

使用 promises 索引映射 Promise.all 输出

Amphp之Promise组合器

Amphp之Promise组合器

Amphp之Iterators(迭代器)