Skip to content

Commit 7438d56

Browse files
committed
Added await promise chain
1 parent 7884b1e commit 7438d56

File tree

6 files changed

+112
-67
lines changed

6 files changed

+112
-67
lines changed

src/Api/Async.php

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace Rcalicdan\FiberAsync\Api;
44

55
use Rcalicdan\FiberAsync\Async\AsyncOperations;
6-
use Rcalicdan\FiberAsync\Loop\LoopOperations;
76
use Rcalicdan\FiberAsync\Promise\Interfaces\PromiseInterface;
87

98
/**
@@ -22,13 +21,8 @@ final class Async
2221
/**
2322
* @var AsyncOperations|null Cached instance of core async operations handler
2423
*/
25-
private static ?AsyncOperations $asyncOps = null;
26-
27-
/**
28-
* @var LoopOperations|null Cached instance of loop operations handler
29-
*/
30-
private static ?LoopOperations $loopOps = null;
3124

25+
private static ?AsyncOperations $asyncOps = null;
3226
/**
3327
* Get the singleton instance of AsyncOperations with lazy initialization.
3428
*
@@ -43,20 +37,6 @@ protected static function getAsyncOperations(): AsyncOperations
4337
return self::$asyncOps;
4438
}
4539

46-
/**
47-
* Get the singleton instance of LoopOperations with lazy initialization.
48-
*
49-
* @return LoopOperations The loop operations handler with automatic lifecycle management
50-
*/
51-
protected static function getLoopOperations(): LoopOperations
52-
{
53-
if (self::$loopOps === null) {
54-
self::$loopOps = new LoopOperations(self::getAsyncOperations());
55-
}
56-
57-
return self::$loopOps;
58-
}
59-
6040
/**
6141
* Reset all cached instances to their initial state.
6242
*
@@ -67,7 +47,6 @@ protected static function getLoopOperations(): LoopOperations
6747
public static function reset(): void
6848
{
6949
self::$asyncOps = null;
70-
self::$loopOps = null;
7150
}
7251

7352
/**

src/MySQL/MySQLPool.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
namespace Rcalicdan\FiberAsync\MySQL;
44

55
use Rcalicdan\FiberAsync\Api\Async;
6-
use Rcalicdan\FiberAsync\Api\Promise;
76
use Rcalicdan\FiberAsync\Promise\Interfaces\PromiseInterface;
8-
use Rcalicdan\FiberAsync\Promise\Promise as AsyncPromise;
7+
use Rcalicdan\FiberAsync\Promise\Promise;
98
use SplQueue;
109

1110
/**
@@ -33,7 +32,7 @@ public function __construct(array $connectionParams, int $maxSize = 10)
3332
public function get(): PromiseInterface
3433
{
3534
if (! $this->pool->isEmpty()) {
36-
return Promise::resolve($this->pool->dequeue());
35+
return Promise::resolved($this->pool->dequeue());
3736
}
3837

3938
// If we haven't reached max connections, create a new one
@@ -55,7 +54,7 @@ public function get(): PromiseInterface
5554
}
5655

5756
// Pool is full, wait for a connection to be released
58-
$promise = new AsyncPromise;
57+
$promise = new Promise;
5958
$this->waiters->enqueue($promise);
6059

6160
return $promise;
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace Rcalicdan\FiberAsync\Promise\Handlers;
4+
5+
use Exception;
6+
use Rcalicdan\FiberAsync\EventLoop\EventLoop;
7+
use Rcalicdan\FiberAsync\Promise\Interfaces\PromiseInterface;
8+
use Throwable;
9+
10+
final class AwaitHandler
11+
{
12+
/**
13+
* Block execution until the promise resolves and return the value.
14+
*
15+
* @template TValue
16+
* @param PromiseInterface<TValue> $promise
17+
* @param bool $resetEventLoop Whether to reset the event loop after completion (default: true)
18+
* @return TValue
19+
* @throws Throwable
20+
*/
21+
public function await(PromiseInterface $promise, bool $resetEventLoop = true): mixed
22+
{
23+
try {
24+
if ($promise->isResolved()) {
25+
return $promise->getValue();
26+
}
27+
28+
if ($promise->isRejected()) {
29+
$reason = $promise->getReason();
30+
throw $reason instanceof Throwable ? $reason : new Exception($this->safeStringCast($reason));
31+
}
32+
33+
$result = null;
34+
$error = null;
35+
$completed = false;
36+
37+
$promise
38+
->then(function ($value) use (&$result, &$completed) {
39+
$result = $value;
40+
$completed = true;
41+
return $value;
42+
})
43+
->catch(function ($reason) use (&$error, &$completed) {
44+
$error = $reason;
45+
$completed = true;
46+
return $reason;
47+
});
48+
49+
while (!$completed) {
50+
EventLoop::getInstance()->run();
51+
}
52+
53+
if ($error !== null) {
54+
throw $error instanceof Throwable ? $error : new Exception($this->safeStringCast($error));
55+
}
56+
57+
return $result;
58+
} finally {
59+
if ($resetEventLoop) {
60+
EventLoop::reset();
61+
}
62+
}
63+
}
64+
65+
/**
66+
* Safely convert mixed value to string for error messages
67+
*/
68+
private function safeStringCast(mixed $value): string
69+
{
70+
return match (true) {
71+
is_string($value) => $value,
72+
is_null($value) => 'null',
73+
is_scalar($value) => (string) $value,
74+
is_object($value) && method_exists($value, '__toString') => (string) $value,
75+
is_array($value) => 'Array: ' . json_encode($value),
76+
is_object($value) => 'Object: ' . get_class($value),
77+
default => 'Unknown error type: ' . gettype($value)
78+
};
79+
}
80+
}

src/Promise/Interfaces/PromiseInterface.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,13 @@ public function getValue(): mixed;
115115
* @throws LogicException If called on a non-rejected promise.
116116
*/
117117
public function getReason(): mixed;
118+
119+
/**
120+
* Block execution until the promise resolves and return the value.
121+
*
122+
* @param bool $resetEventLoop Whether to reset the event loop after completion (default: true)
123+
* @return TValue The resolved value
124+
* @throws \Throwable The rejection reason if promise was rejected
125+
*/
126+
public function await(bool $resetEventLoop = true): mixed;
118127
}

src/Promise/Promise.php

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Rcalicdan\FiberAsync\Promise;
44

55
use Rcalicdan\FiberAsync\Async\AsyncOperations;
6+
use Rcalicdan\FiberAsync\Promise\Handlers\AwaitHandler;
67
use Rcalicdan\FiberAsync\Promise\Handlers\CallbackHandler;
78
use Rcalicdan\FiberAsync\Promise\Handlers\ChainHandler;
89
use Rcalicdan\FiberAsync\Promise\Handlers\ExecutorHandler;
@@ -50,6 +51,11 @@ class Promise implements PromiseCollectionInterface, PromiseInterface
5051
*/
5152
private ResolutionHandler $resolutionHandler;
5253

54+
/**
55+
* @var AwaitHandler
56+
*/
57+
private AwaitHandler $awaitHandler;
58+
5359
/**
5460
* @var CancellablePromiseInterface<mixed>|null
5561
*/
@@ -75,18 +81,27 @@ public function __construct(?callable $executor = null)
7581
$this->callbackHandler = new CallbackHandler;
7682
$this->executorHandler = new ExecutorHandler;
7783
$this->chainHandler = new ChainHandler;
84+
$this->awaitHandler = new AwaitHandler;
7885
$this->resolutionHandler = new ResolutionHandler(
7986
$this->stateHandler,
8087
$this->callbackHandler
8188
);
8289

8390
$this->executorHandler->executeExecutor(
8491
$executor,
85-
fn ($value) => $this->resolve($value),
86-
fn ($reason) => $this->reject($reason)
92+
fn($value) => $this->resolve($value),
93+
fn($reason) => $this->reject($reason)
8794
);
8895
}
8996

97+
/**
98+
* {@inheritdoc}
99+
*/
100+
public function await(bool $resetEventLoop = true): mixed
101+
{
102+
return $this->awaitHandler->await($this, $resetEventLoop);
103+
}
104+
90105
/**
91106
* {@inheritdoc}
92107
*/
@@ -187,9 +202,9 @@ function (callable $resolve, callable $reject) use ($onFulfilled, $onRejected) {
187202
};
188203

189204
if ($this->stateHandler->isResolved()) {
190-
$this->chainHandler->scheduleHandler(fn () => $handleResolve($this->stateHandler->getValue()));
205+
$this->chainHandler->scheduleHandler(fn() => $handleResolve($this->stateHandler->getValue()));
191206
} elseif ($this->stateHandler->isRejected()) {
192-
$this->chainHandler->scheduleHandler(fn () => $handleReject($this->stateHandler->getReason()));
207+
$this->chainHandler->scheduleHandler(fn() => $handleReject($this->stateHandler->getReason()));
193208
} else {
194209
$this->callbackHandler->addThenCallback($handleResolve);
195210
$this->callbackHandler->addCatchCallback($handleReject);

test.php

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)