Skip to content

Commit ff11a7a

Browse files
authored
Merge pull request #18 from WyriHaximus-labs/fibers-fastforward-resolved-promise
Fast forward resolved/rejected promises with fibers await
2 parents 97a6ad3 + 546cb73 commit ff11a7a

File tree

4 files changed

+67
-11
lines changed

4 files changed

+67
-11
lines changed

src/FiberInterface.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ interface FiberInterface
1717
{
1818
public function resume(mixed $value): void;
1919

20-
public function throw(mixed $throwable): void;
20+
public function throw(\Throwable $throwable): void;
2121

2222
public function suspend(): mixed;
2323
}

src/SimpleFiber.php

+1-7
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,8 @@ public function resume(mixed $value): void
2727
Loop::futureTick(fn() => $this->fiber->resume($value));
2828
}
2929

30-
public function throw(mixed $throwable): void
30+
public function throw(\Throwable $throwable): void
3131
{
32-
if (!$throwable instanceof \Throwable) {
33-
$throwable = new \UnexpectedValueException(
34-
'Promise rejected with unexpected value of type ' . (is_object($throwable) ? get_class($throwable) : gettype($throwable))
35-
);
36-
}
37-
3832
if ($this->fiber === null) {
3933
Loop::futureTick(static fn() => \Fiber::suspend(static fn() => throw $throwable));
4034
return;

src/functions.php

+35-3
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,49 @@ function async(callable $function): callable
7777
*/
7878
function await(PromiseInterface $promise): mixed
7979
{
80-
$fiber = FiberFactory::create();
80+
$fiber = null;
81+
$resolved = false;
82+
$rejected = false;
83+
$resolvedValue = null;
84+
$rejectedThrowable = null;
8185

8286
$promise->then(
83-
function (mixed $value) use (&$resolved, $fiber): void {
87+
function (mixed $value) use (&$resolved, &$resolvedValue, &$fiber): void {
88+
if ($fiber === null) {
89+
$resolved = true;
90+
$resolvedValue = $value;
91+
return;
92+
}
93+
8494
$fiber->resume($value);
8595
},
86-
function (mixed $throwable) use (&$resolved, $fiber): void {
96+
function (mixed $throwable) use (&$rejected, &$rejectedThrowable, &$fiber): void {
97+
if (!$throwable instanceof \Throwable) {
98+
$throwable = new \UnexpectedValueException(
99+
'Promise rejected with unexpected value of type ' . (is_object($throwable) ? get_class($throwable) : gettype($throwable))
100+
);
101+
}
102+
103+
if ($fiber === null) {
104+
$rejected = true;
105+
$rejectedThrowable = $throwable;
106+
return;
107+
}
108+
87109
$fiber->throw($throwable);
88110
}
89111
);
90112

113+
if ($resolved) {
114+
return $resolvedValue;
115+
}
116+
117+
if ($rejected) {
118+
throw $rejectedThrowable;
119+
}
120+
121+
$fiber = FiberFactory::create();
122+
91123
return $fiber->suspend();
92124
}
93125

tests/AwaitTest.php

+30
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,36 @@ public function testAwaitShouldNotCreateAnyGarbageReferencesForPromiseRejectedWi
157157
$this->assertEquals(0, gc_collect_cycles());
158158
}
159159

160+
/**
161+
* @dataProvider provideAwaiters
162+
*/
163+
public function testAlreadyFulfilledPromiseShouldNotSuspendFiber(callable $await)
164+
{
165+
for ($i = 0; $i < 6; $i++) {
166+
$this->assertSame($i, $await(React\Promise\resolve($i)));
167+
}
168+
}
169+
170+
/**
171+
* @dataProvider provideAwaiters
172+
*/
173+
public function testNestedAwaits(callable $await)
174+
{
175+
$this->assertTrue($await(new Promise(function ($resolve) use ($await) {
176+
$resolve($await(new Promise(function ($resolve) use ($await) {
177+
$resolve($await(new Promise(function ($resolve) use ($await) {
178+
$resolve($await(new Promise(function ($resolve) use ($await) {
179+
$resolve($await(new Promise(function ($resolve) use ($await) {
180+
Loop::addTimer(0.01, function () use ($resolve) {
181+
$resolve(true);
182+
});
183+
})));
184+
})));
185+
})));
186+
})));
187+
})));
188+
}
189+
160190
public function provideAwaiters(): iterable
161191
{
162192
yield 'await' => [static fn (React\Promise\PromiseInterface $promise): mixed => React\Async\await($promise)];

0 commit comments

Comments
 (0)