phpgt / promise
愉快地处理异步代码。
Requires
- php: >=8.1
Requires (Dev)
- phpmd/phpmd: ^2.13
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.1
- squizlabs/php_codesniffer: ^3.7
This package is auto-updated.
Last update: 2024-09-05 15:03:41 UTC
README
关于Promise概念有许多实现。这个库旨在与Web API的Promise实现兼容,提供与在浏览器中处理Promise时相同的then
、catch
和finally
机制。
在计算机科学中,Promise是一种机制,它提供了程序性代码与异步回调之间简单直接的关系。在程序性语言中,如传统的PHP,函数有两种方式可以影响程序的流程:要么返回值,要么抛出异常。
当处理执行异步的函数时,我们无法返回值,因为它们可能还没有准备好,我们也无法抛出异常,因为这是一个程序性概念(我们应当在何处捕获它们?)。这就是Promise的作用所在:不是返回值或抛出异常,你的函数可以返回一个Promise,这是一个对象,可以被一个值履行,或被一个异常拒绝,但并不一定是在它们返回的时候。
使用这个概念,实际计算或加载代码所需值的任务可以被延迟到执行异步的任务中。在PHP.Gt/Promise的背后,有一个用于此目的的Deferred
类。
示例用法
以下是这个库提供的语法示例。
// A simple operation with just a single "then": $exampleSlowFileReader->read() ->then(function(string $contents) { echo "Contents of file: $contents", PHP_EOL; }); // A more complex example, showing how promises can be chained together: $exampleRemoteApi->getCustomerById(105) ->then(function(Customer $customer) { return $customer->loadLatestOrders(new DateTime("-5 weeks")); }) ->then(function(CustomerOrderList $orders) { echo "Customer {$orders->getCustomer()->getName()} ", "has made {count($orders)} in the last 5 weeks!", PHP_EOL; }) ->catch(function(Throwable $reason) { echo "There was an error loading the customer's details: $reason", PHP_EOL; }) ->finally(function() use($exampleRemoteApi) { $exampleRemoteApi->disconnect(); });
Deferred
和 Promise
对象
这个仓库将异步任务的处理责任和任务完成的输出分别分配给了Deferred
和Promise
类。
Deferred
对象分配了一个或多个“处理”回调,这些回调将被按顺序调用以执行延迟任务。
Promise
在构造时由Deferred
创建,用于表示延迟任务完成的输出。
为了使类与Promise一起工作,它至少需要两个函数:一个公共函数,用于构建Deferred对象并返回Promise,以及一个作为Deferred处理函数分配的函数。
请参阅下面的示例代码布局
class Example { // The class must keep a reference to its own Deferred object, as this will be // referenced when the work completes in order to resolve its Promise. private Deferred $deferred; // This public function will return a Promise representing the task's outcome. public function doTheTask():PromiseInterface { // The Deferred is constructed with the process function as its only parameter. $this->deferred = new Deferred(fn() => $this->processFunction()); // The function returns the Deferred's promise. return $this->deferred->getPromise(); } // The process function will do one small piece of work each time it's called // until the task is complete, at which point it will resolve the Deferred with // the final value (from wherever it's calculated). private function processFunction():void { if($this->isThereMoreWorkToDo()) { $this->doMoreWork(); } else { $this->deferred->resolve($this->getFinalValue()); } } private function isThereMoreWorkToDo():bool { /* ... */ } private function doMoreWork():void { /* ... */ } private function getFinalValue() { /* ... */ } }
上述示例类如何整合的最简单表示如下
$example = new Example(); $example->doTheTask() ->then(function($finalValue) { echo "Final value: ", $finalValue; });
最终结果是能够调用公共函数,而不需要了解Promise/Deferred实现。
重要提示:PHP仍然是一种完全的程序性语言,因此,如果没有使用外部循环来调用延迟处理,Promise永远不会履行或拒绝。
事件循环
为了使这个Promise库有用,必须有某些代码充当事件循环来调用延迟过程。这可以是一个简单的while循环,但在现实世界的任务中,应该使用更全面的循环系统。
基于Promise的架构实现本身就足够复杂,因此事件循环库的责任被单独维护在PHP.Gt/Async中。
特别感谢
这个仓库的开发工作主要归功于reactphp的Promise实现和Domenic Denicola出色的写作所带来的巨大工作和灵感。