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
对象被分配一个或多个“处理”回调,这些回调将按顺序调用以执行延迟的任务。
由Deferred
创建一个Promise,在构造时使用,用于表示延迟任务完成的最终结果。
为了使类与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的承诺实现(reactphp的承诺实现)以及Domenic Denicola出色的写作(Domenic Denicola的出色写作)所带来的巨大工作和灵感。