Promise

PHP的 CommonJS Promises/A 的轻量级实现。

Build Status Coverage Status

主分支包含即将发布的3.0版本的代码。 对于当前稳定的2.x版本的代码,请检查 2.x分支

即将发布的3.0版本将是此软件包的主发展方向。 但是,对于尚未安装PHP 7+的用户,我们仍将积极支持2.0和1.0。

目录

  1. 简介
  2. 概念
  3. API
  4. 示例
  5. Credits
  6. License

简介


Promise是一个为PHP实现CommonJS Promises/A 的库。

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

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

概念


Deferred(延迟)

Deferred 表示可能尚未完成的计算或工作单元。 通常(但并非总是)该计算将异步执行并在将来的某个时刻完成。

Promise(承诺)

Deferred 表示计算本身,而 Promise 表示该计算的结果。 因此,每个Deferred 者都有一个承诺,可以充当其实际结果的占位符。

API

Deferred

Deferred 表示其解析挂起的操作。它有单独的promise和resolver部分。

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

$promise = $deferred->promise();

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

promise 方法返回延迟的承诺。

resolvereject 方法控制延迟状态。

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

Deferred::promise()

$promise = $deferred->promise();

返回延期的承诺,您可以将其分发给其他人,同时保留自行修改其状态的权限。

Deferred::resolve()

$deferred->resolve(mixed $value = null);

解决promise()返回的承诺。

$value调用 $onFulfilled来通知所有消费者(它们通过$promise->then() 注册)。

如果$value本身是一个承诺,那么一旦这个承诺被解决,它将转换到这个承诺的状态。

Deferred::reject()

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

拒绝promise()返回的承诺,表示延迟的计算失败。 所有消费者都会收到通知,方法是使用$reason调用$onRejected(它们通过$promise->then()注册)。

PromiseInterface

PromiseInterface 为所有promise实现提供公共接口。 请参阅Promise以获取此包唯一公共实现。

一个承诺代表一个最终的结果,要么是实现(成功)和相关的结果,要么是拒绝(失败)和相关的原因。

一旦承诺处于履行或拒绝的状态,承诺就变得不可更改。 它的状态和结果(或错误)都不能修改。

PromiseInterface::then()

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

通过对承诺的履行或拒绝的结果,应用函数来转换承诺的状态值并返回转换结果的新承诺。

then() 方法用于一个promise注册新的已完成和拒绝处理程序(所有参数都是可选的):

  • 一旦承诺实现,其结果将作为第一个参数传递,就会调用$onFulfilled
  • 一旦承诺被拒绝,其原因作为第一个参数传递,就会调用$onRejected

$onFulfilled$onRejected的返回一个新的promise,无论哪个被调用,或者其中一个抛出异常则抛出异常。

promise 对在then()的同一调用中注册的处理回调做出以下保证:

1.仅会调用$onFulfilled$onRejected中的一个,两者不会都调用。

2.$onFulfilled$onRejected 不会被调用一次以上。

另请参阅

PromiseInterface::done()

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

如果承诺履行或拒绝,则耗费承诺的最终结果。

如果 $onFulfilled$onRejected抛出或返回被拒绝的诺言,将导致致命错误(E_USER_ERROR)

由于done()的目的是消费而不是转换,所以done()总是返回null

另请参阅

PromiseInterface::otherwise()

$promise->otherwise(callable $onRejected);

注册拒绝处理程序以进行承诺。 快捷操作方式如下:

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

另外,您可以传入提示$onRejected$reason参数,仅捕获特定的错误。

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

PromiseInterface::always()

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

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

promise被履行或被拒绝时调用$onFulfilledOrRejected(不带任何参数)回调

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

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

考虑以下同步代码:

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

可以编写类似的异步代码(doSomething()返回承诺):

return doSomething()
    ->otherwise('handleError')
    ->always('cleanup');

PromiseInterface::cancel()

$promise->cancel();

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

一旦兑现了承诺(无论是履行还是拒绝),对承诺调用cancel()无效。

Promise

创建一个promise,其状态由传$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 React\Promise\Promise($resolver, $canceller);

promise构造函数将接收一个resolver函数和一个可选的canceller函数,这两个函数都将使用3个参数进行调用:

  • $resolve($value) - 包装返回的promise的主要函数。 接受非承诺值或其他承诺。 当用非承诺值调用时,用该值实现承诺。当以另一个承诺被调用时, 例如 $resolve($otherPromise),包装返回的promise等同于$otherPromise
    • $rejec($reason) - 拒绝承诺的函数。建议只抛出异常,而不要使用$reject()

如果resolvercanceller抛出异常,则将以该抛出的异常作为拒绝原因来拒绝承诺。

resolver函数将立即被调用,只有当所有使用者调用promisecancel()方法时,canceller函数才会被调用。

Functions

用于创建、连接、映射和减少承诺集合的有用函数。

所有处理承诺集合的功能(例如all(),race(),some()等)都支持取消。 这意味着,如果您对返回的诺言调用cancel(),则集合中的所有诺言都会被取消。

resolve()

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

$promiseOrValue创建一个承诺。

如果$promiseOrValue是一个值,它将是返回承诺

如果$promiseOrValue拥有thenable能力(提供then()方法的对象),则返回跟随thenable状态的可信承诺。

如果$promiseOrValue是一个承诺,它将按原样返回。

reject()

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

$reason创建一个拒绝的promise

注意\Throwable PHP7中引入的接口包括两个用户区域 \Exception\Error 内部PHP错误。 通过强制使用\Throwable作为拒绝承诺的理由,任何语言错误或用户地异常都可以用来拒绝承诺。

all()

$promise = React\Promise\all(array $promisesOrValues);

返回一个承诺,该承诺仅在$promisesOrValues中的所有项都已解析时才会解析。 返回的承诺的解析值将是一个数组,其中包含$promisesOrValues中每个项的解析值。

race()

$promise = React\Promise\race(array $promisesOrValues);

发起一场允许一名获胜者参加的竞赛。返回一个承诺,该承诺的解决方式与第一个履行的承诺解决方式相同。

如果$promisesOrValues包含0项,则返回的承诺将无限挂起

any()

$promise = React\Promise\any(array $promisesOrValues);

返回将在$promisesOrValues中的任何一项兑现的承诺。返回的承诺的值将是触发项的值。

只有在$promisesOrValues中的 所有 项被拒绝时,返回的承诺才会被拒绝。 拒绝值将是React\Promise\Exception\CompositeException,其中包含所有拒绝原因。 拒绝原因可以通过CompositeException::getThrowables()获得。

如果$promisesOrValues包含0项,则返回的承诺也将拒绝, 并带有React\Promise\Exception\LengthException

some()

$promise = React\Promise\some(array $promisesOrValues, integer $howMany);

返回一个承诺,当$promisesOrValues中至少有$howMany个提供的项兑现时,该承诺将被兑现。 返回的承诺的值将是一个长度为$howMany的数组,该数组包含首先解析的$howMany已兑现承诺的值。

如果$howMany项无法兑现(即(count($promisesOrValues)-$howMany)+1项拒绝), 则返回的承诺将拒绝。拒绝值将是一个React\Promise\Exception\CompositeException, 其中包含(count($promisesOrValues)-$howMany)+1拒绝原因。

拒绝原因可以通过CompositeException::getExceptions()获得。

如果$promisesOrValues包含的项目少于$howMany,则返回的承诺也将被拒绝, 并带有React\Promise\Exception\LengthException

map()

$promise = React\Promise\map(array $promisesOrValues, callable $mapFunc);

传统的map函数,类似于array_map(),但允许输入包含承诺 和/或 值,$mapFunc可以返回值或承诺。

map函数接收每个项作为参数,其中item是$promisesOrValues中的promise或value的完全解析值。

reduce()

$promise = React\Promise\reduce(array $promisesOrValues, callable $reduceFunc, $initialValue = null);

传统的reduce函数,类似于array_reduce(),但输入可以包含承诺 和/或 值, $reduceFunc可以返回值或承诺 $initialValue可以是承诺或起始值。

PromisorInterface

React\Promise\PromisorInterface提供实现承诺的公共接口。 React\Promise\Deferred实现了它,但是由于它是公共API的一部分,任何人都可以实现它。

示例

如何使用Deferred

function getAwesomeResultPromise()
{
    $deferred = new React\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
        }
    );

承诺转发的工作原理

几个简单的例子来展示promise/A转发机制是如何工作的。 当然,这些示例是精心设计的,在实际使用中,承诺链通常会分布在几个函数调用中,甚至是应用程序架构的几个级别。

转发履行

已履行承诺将值转发给下一个承诺。 第一个承诺$deferred->promise()将用下面传递给$deferred->resolve()的值来进行解析。

每次调用then()都会返回一个新的承诺,该承诺将使用前一个处理程序的返回值进行解析。这里创建了一个承诺“管道”。

$deferred = new React\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"

转发拒绝

被拒绝的承诺的行为与try/catch类似,工作方式也与此类似:当你捕获一个异常时,你必须重新抛出进而继续向下传播。

同样,当您处理被拒绝的承诺时且传播拒绝,需通过返回被拒绝的承诺或实际抛出“重新抛出”它(因为promise将抛出的异常转换为拒绝)

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

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

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

转发履行和拒绝

就像try/catch一样,您可以选择是否传播。转发履行和拒绝仍将以可预测的方式转发回调结果。

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

$deferred->promise()
    ->then(function ($x) {
        return $x + 1;
    })
    ->then(function ($x) {
        throw new \Exception($x + 1);
    })
    ->otherwise(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"

done() vs. then()

黄金法则是:

返回您的诺言,或调用done()方法.

乍一看,then()done()看起来非常相似。 但是,有重要的区别。

then()的目的是转换promise的值,并将转换后的值传递或返回一个新的promise到代码的其他部分。

done()的目的是消费promise的值,并将转换后的值转移到代码中。

除了转换值之外,then()还允许您从中间错误中恢复或传播。 未处理的任何错误都将由Promise机制捕获,并用于拒绝then()返回的承诺。

调用done()将错误的所有责任转移到代码中。 如果错误(抛出的异常或返回的拒绝)未能在您提供done()$onFulfilled$onRejected回调中捕获,它将导致致命错误。

function getJsonResult()
{
    return queryApi()
        ->then(
            // Transform API results to an object
            function ($jsonResultString) {
                return json_decode($jsonResultString);
            },
            // Transform API errors to an exception
            function ($jsonErrorString) {
                $object = json_decode($jsonErrorString);
                throw new ApiErrorException($object->errorMessage);
            }
        );
}

// Here we provide no rejection handler. If the promise returned has been
// rejected, the ApiErrorException will be thrown
getJsonResult()
    ->done(
        // Consume transformed object
        function ($jsonResultObject) {
            // Do something with $jsonResultObject
        }
    );

// Here we provide a rejection handler which will either throw while debugging
// or log the exception
getJsonResult()
    ->done(
        function ($jsonResultObject) {
            // Do something with $jsonResultObject
        },
        function (ApiErrorException $exception) {
            if (isDebug()) {
                throw $exception;
            } else {
                logException($exception);
            }
        }
    );

Credits

承诺是 when.js 的一部分,作者Brian Cavalier.

而且,大部分文档都是来源于when.js WikiAPI docs.

License

Released under the MIT license.

results matching ""

    No results matching ""

    results matching ""

      No results matching ""