mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Rewrite SwoolePromise to be fully synchronous
Remove all coroutine-based execution. The executor now runs synchronously in the constructor, matching graphql-php's SyncPromise behavior. This ensures all promise operations complete immediately without scheduling issues. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,11 +2,21 @@
|
||||
|
||||
namespace Appwrite\Promises;
|
||||
|
||||
/**
|
||||
* Swoole-compatible promise implementation that runs synchronously
|
||||
* but can wait for external async operations via Swoole channels.
|
||||
*/
|
||||
class Swoole extends Promise
|
||||
{
|
||||
public const PENDING = 'pending';
|
||||
public const FULFILLED = 'fulfilled';
|
||||
public const REJECTED = 'rejected';
|
||||
|
||||
public string $promiseState = self::PENDING;
|
||||
public mixed $promiseResult = null;
|
||||
|
||||
/**
|
||||
* Callbacks waiting for this promise to settle
|
||||
* Each entry is [Promise, callable|null, callable|null]
|
||||
*
|
||||
* @var array<array{self, callable|null, callable|null}>
|
||||
*/
|
||||
@@ -18,14 +28,14 @@ class Swoole extends Promise
|
||||
return;
|
||||
}
|
||||
|
||||
$resolve = function ($value) {
|
||||
$this->doResolve($value);
|
||||
};
|
||||
$reject = function ($reason) {
|
||||
$this->doReject($reason);
|
||||
};
|
||||
|
||||
$this->execute($executor, $resolve, $reject);
|
||||
try {
|
||||
$executor(
|
||||
fn($value) => $this->doResolve($value),
|
||||
fn($reason) => $this->doReject($reason)
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
$this->doReject($e);
|
||||
}
|
||||
}
|
||||
|
||||
protected function execute(
|
||||
@@ -33,21 +43,12 @@ class Swoole extends Promise
|
||||
callable $resolve,
|
||||
callable $reject
|
||||
): void {
|
||||
\go(function () use ($executor, $resolve, $reject) {
|
||||
try {
|
||||
$executor($resolve, $reject);
|
||||
} catch (\Throwable $exception) {
|
||||
$reject($exception);
|
||||
}
|
||||
});
|
||||
// Not used - we execute synchronously in constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal resolve that triggers waiting callbacks
|
||||
*/
|
||||
protected function doResolve(mixed $value): void
|
||||
{
|
||||
if ($this->state !== self::STATE_PENDING) {
|
||||
if ($this->promiseState !== self::PENDING) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,44 +61,36 @@ class Swoole extends Promise
|
||||
return;
|
||||
}
|
||||
|
||||
$this->result = $value;
|
||||
$this->state = self::STATE_FULFILLED;
|
||||
$this->promiseState = self::FULFILLED;
|
||||
$this->promiseResult = $value;
|
||||
$this->processWaiting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal reject that triggers waiting callbacks
|
||||
*/
|
||||
protected function doReject(mixed $reason): void
|
||||
{
|
||||
if ($this->state !== self::STATE_PENDING) {
|
||||
if ($this->promiseState !== self::PENDING) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->result = $reason;
|
||||
$this->state = self::STATE_REJECTED;
|
||||
$this->promiseState = self::REJECTED;
|
||||
$this->promiseResult = $reason;
|
||||
$this->processWaiting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process all waiting callbacks
|
||||
*/
|
||||
protected function processWaiting(): void
|
||||
{
|
||||
foreach ($this->waiting as [$promise, $onFulfilled, $onRejected]) {
|
||||
$callback = $this->state === self::STATE_FULFILLED ? $onFulfilled : $onRejected;
|
||||
$callback = $this->promiseState === self::FULFILLED ? $onFulfilled : $onRejected;
|
||||
|
||||
if ($callback === null) {
|
||||
// Pass through the value/reason
|
||||
if ($this->state === self::STATE_FULFILLED) {
|
||||
$promise->doResolve($this->result);
|
||||
if ($this->promiseState === self::FULFILLED) {
|
||||
$promise->doResolve($this->promiseResult);
|
||||
} else {
|
||||
$promise->doReject($this->result);
|
||||
$promise->doReject($this->promiseResult);
|
||||
}
|
||||
} else {
|
||||
// Run callback synchronously
|
||||
try {
|
||||
$result = $callback($this->result);
|
||||
$result = $callback($this->promiseResult);
|
||||
$promise->doResolve($result);
|
||||
} catch (\Throwable $e) {
|
||||
$promise->doReject($e);
|
||||
@@ -107,32 +100,26 @@ class Swoole extends Promise
|
||||
$this->waiting = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Override then to use callback-based approach instead of busy-waiting
|
||||
*/
|
||||
public function then(
|
||||
?callable $onFulfilled = null,
|
||||
?callable $onRejected = null
|
||||
): self {
|
||||
$promise = new self();
|
||||
|
||||
if ($this->state === self::STATE_PENDING) {
|
||||
// Queue the callbacks for later
|
||||
if ($this->promiseState === self::PENDING) {
|
||||
$this->waiting[] = [$promise, $onFulfilled, $onRejected];
|
||||
} else {
|
||||
// Already settled, process immediately
|
||||
$callback = $this->state === self::STATE_FULFILLED ? $onFulfilled : $onRejected;
|
||||
$callback = $this->promiseState === self::FULFILLED ? $onFulfilled : $onRejected;
|
||||
|
||||
if ($callback === null) {
|
||||
if ($this->state === self::STATE_FULFILLED) {
|
||||
$promise->doResolve($this->result);
|
||||
if ($this->promiseState === self::FULFILLED) {
|
||||
$promise->doResolve($this->promiseResult);
|
||||
} else {
|
||||
$promise->doReject($this->result);
|
||||
$promise->doReject($this->promiseResult);
|
||||
}
|
||||
} else {
|
||||
// Run callback synchronously
|
||||
try {
|
||||
$result = $callback($this->result);
|
||||
$result = $callback($this->promiseResult);
|
||||
$promise->doResolve($result);
|
||||
} catch (\Throwable $e) {
|
||||
$promise->doReject($e);
|
||||
@@ -143,30 +130,38 @@ class Swoole extends Promise
|
||||
return $promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override resolve to use internal method
|
||||
*/
|
||||
public function resolve(mixed $value): self
|
||||
{
|
||||
$this->doResolve($value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override reject to use internal method
|
||||
*/
|
||||
public function reject(mixed $reason): self
|
||||
{
|
||||
$this->doReject($reason);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that completes when all passed in promises complete.
|
||||
*
|
||||
* @param iterable $promisesOrValues Array of promises and/or plain values
|
||||
* @return self
|
||||
*/
|
||||
public function isPending(): bool
|
||||
{
|
||||
return $this->promiseState === self::PENDING;
|
||||
}
|
||||
|
||||
public function isFulfilled(): bool
|
||||
{
|
||||
return $this->promiseState === self::FULFILLED;
|
||||
}
|
||||
|
||||
public function isRejected(): bool
|
||||
{
|
||||
return $this->promiseState === self::REJECTED;
|
||||
}
|
||||
|
||||
public function getResult(): mixed
|
||||
{
|
||||
return $this->promiseResult;
|
||||
}
|
||||
|
||||
public static function all(iterable $promisesOrValues): self
|
||||
{
|
||||
$promisesOrValues = \is_array($promisesOrValues)
|
||||
@@ -185,7 +180,7 @@ class Swoole extends Promise
|
||||
$result = [];
|
||||
$rejected = false;
|
||||
|
||||
$resolveIfDone = static function () use (&$count, $total, &$result, &$rejected, $promise): void {
|
||||
$checkComplete = static function () use (&$count, $total, &$result, &$rejected, $promise): void {
|
||||
if (!$rejected && $count === $total) {
|
||||
\ksort($result);
|
||||
$promise->doResolve($result);
|
||||
@@ -193,13 +188,13 @@ class Swoole extends Promise
|
||||
};
|
||||
|
||||
foreach ($promisesOrValues as $index => $promiseOrValue) {
|
||||
if ($promiseOrValue instanceof Promise) {
|
||||
if ($promiseOrValue instanceof self) {
|
||||
$result[$index] = null;
|
||||
$promiseOrValue->then(
|
||||
static function ($value) use (&$result, $index, &$count, $resolveIfDone) {
|
||||
static function ($value) use (&$result, $index, &$count, $checkComplete) {
|
||||
$result[$index] = $value;
|
||||
++$count;
|
||||
$resolveIfDone();
|
||||
$checkComplete();
|
||||
return $value;
|
||||
},
|
||||
static function ($error) use (&$rejected, $promise) {
|
||||
@@ -215,7 +210,7 @@ class Swoole extends Promise
|
||||
}
|
||||
}
|
||||
|
||||
$resolveIfDone();
|
||||
$checkComplete();
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user