mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Fix batch promises with coroutine-based synchronization
- Use Swoole Coroutine::sleep for proper coroutine yielding instead of callback-based completion tracking - Spawn a coroutine for each promise to wait for its completion - Use Channel for synchronization between coroutines - Add public accessors to Promise class for state checking Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
4d6a3d36f7
commit
2544baa669
@@ -5,6 +5,7 @@ namespace Appwrite\GraphQL\Promises\Adapter;
|
||||
use Appwrite\GraphQL\Promises\Adapter;
|
||||
use Appwrite\Promises\Swoole as SwoolePromise;
|
||||
use GraphQL\Executor\Promise\Promise as GQLPromise;
|
||||
use Swoole\Coroutine\Channel;
|
||||
|
||||
class Swoole extends Adapter
|
||||
{
|
||||
@@ -46,40 +47,50 @@ class Swoole extends Adapter
|
||||
return;
|
||||
}
|
||||
|
||||
$result = \array_fill(0, $count, null);
|
||||
$pending = $count;
|
||||
$rejected = false;
|
||||
$result = [];
|
||||
$error = null;
|
||||
$channel = new Channel($count);
|
||||
|
||||
foreach ($promisesOrValues as $index => $promiseOrValue) {
|
||||
if ($promiseOrValue instanceof GQLPromise) {
|
||||
$promiseOrValue->then(
|
||||
function ($value) use ($index, &$result, &$pending, &$rejected, $resolve) {
|
||||
if ($rejected) {
|
||||
return;
|
||||
}
|
||||
$result[$index] = $value;
|
||||
$pending--;
|
||||
if ($pending === 0) {
|
||||
\ksort($result);
|
||||
$resolve($result);
|
||||
}
|
||||
},
|
||||
function ($error) use (&$rejected, $reject) {
|
||||
if (!$rejected) {
|
||||
$rejected = true;
|
||||
$reject($error);
|
||||
// Spawn a coroutine to wait for each promise
|
||||
\go(function () use ($promiseOrValue, $index, &$result, &$error, $channel) {
|
||||
/** @var SwoolePromise $adoptedPromise */
|
||||
$adoptedPromise = $promiseOrValue->adoptedPromise;
|
||||
|
||||
// Wait for the promise to resolve using a polling approach
|
||||
while ($adoptedPromise->isPending()) {
|
||||
\Swoole\Coroutine::sleep(0.001);
|
||||
}
|
||||
|
||||
if ($adoptedPromise->isFulfilled()) {
|
||||
$result[$index] = $adoptedPromise->getResult();
|
||||
} else {
|
||||
if ($error === null) {
|
||||
$error = $adoptedPromise->getResult();
|
||||
}
|
||||
}
|
||||
);
|
||||
$channel->push(true);
|
||||
});
|
||||
} else {
|
||||
$result[$index] = $promiseOrValue;
|
||||
$pending--;
|
||||
if ($pending === 0 && !$rejected) {
|
||||
\ksort($result);
|
||||
$resolve($result);
|
||||
}
|
||||
$channel->push(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for all promises to complete
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$channel->pop();
|
||||
}
|
||||
$channel->close();
|
||||
|
||||
if ($error !== null) {
|
||||
$reject($error);
|
||||
return;
|
||||
}
|
||||
|
||||
\ksort($result);
|
||||
$resolve($result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ abstract class Promise
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isPending(): bool
|
||||
public function isPending(): bool
|
||||
{
|
||||
return $this->state == self::STATE_PENDING;
|
||||
}
|
||||
@@ -177,7 +177,7 @@ abstract class Promise
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isFulfilled(): bool
|
||||
public function isFulfilled(): bool
|
||||
{
|
||||
return $this->state == self::STATE_FULFILLED;
|
||||
}
|
||||
@@ -187,8 +187,18 @@ abstract class Promise
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isRejected(): bool
|
||||
public function isRejected(): bool
|
||||
{
|
||||
return $this->state == self::STATE_REJECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result value (only valid after promise is settled)
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResult(): mixed
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user