/** * Emits a value from the observable. The returned promise is resolved with the emitted value once all subscribers * have been invoked. * * @param mixed $value * * @return \Interop\Async\Promise * * @throws \Error If the observable has resolved. */ private function emit($value) : Promise { if ($this->resolved) { throw new \Error("The observable has been resolved; cannot emit more values"); } if ($value instanceof Promise) { $deferred = new Deferred(); $value->when(function ($e, $v) use($deferred) { if ($this->resolved) { $deferred->fail(new \Error("The observable was resolved before the promise result could be emitted")); return; } if ($e) { $this->fail($e); $deferred->fail($e); return; } $deferred->resolve($this->emit($v)); }); return $deferred->promise(); } $promises = []; foreach ($this->subscribers as $onNext) { try { $result = $onNext($value); if ($result instanceof Promise) { $promises[] = $result; } } catch (\Throwable $e) { Loop::defer(static function () use($e) { throw $e; }); } } if (!$promises) { return new Success($value); } $deferred = new Deferred(); $count = \count($promises); $f = static function ($e) use($deferred, $value, &$count) { if ($e) { Loop::defer(static function () use($e) { throw $e; }); } if (!--$count) { $deferred->resolve($value); } }; foreach ($promises as $promise) { $promise->when($f); } return $deferred->promise(); }
/** * Create an artificial timeout for any Promise instance * * If the timeout expires prior to promise resolution the returned * promise is failed. * * @param \Amp\Promise $promise The promise to which the timeout applies * @param int $msTimeout The timeout in milliseconds * @return \Amp\Promise */ function timeout(Promise $promise, $msTimeout) { $resolved = false; $promisor = new Deferred(); $watcherId = once(function () use($promisor, &$resolved) { $resolved = true; $promisor->fail(new TimeoutException("Promise resolution timed out")); }, $msTimeout); $promise->when(function ($error = null, $result = null) use($promisor, $watcherId, &$resolved) { if ($resolved) { return; } $resolved = true; cancel($watcherId); if ($error) { $promisor->fail($error); } else { $promisor->succeed($result); } }); return $promisor->promise(); }
/** * Returns a promise that succeeds or fails when the first promise succeeds or fails. * * @param Promise[] $promises * * @return \Interop\Async\Promise * * @throws \Error If the array is empty or a non-Promise is in the array. */ function choose(array $promises) : Promise { if (empty($promises)) { throw new \Error("No promises provided"); } $deferred = new Deferred(); $resolved = false; foreach ($promises as $promise) { if (!$promise instanceof Promise) { throw new \Error("Non-promise provided"); } $promise->when(function ($exception, $value) use(&$resolved, $deferred) { if ($resolved) { return; } $resolved = true; if ($exception) { $deferred->fail($exception); return; } $deferred->resolve($value); }); } return $deferred->promise(); }