Example #1
0
 /**
  * {@inheritdoc}
  */
 public function when(callable $onResolved)
 {
     try {
         $onResolved(null, $this->value);
     } catch (\Throwable $exception) {
         Loop::defer(static function () use($exception) {
             throw $exception;
         });
     }
 }
Example #2
0
 /**
  * @param \Generator $generator
  */
 public function __construct(\Generator $generator)
 {
     $this->generator = $generator;
     /**
      * @param \Throwable|null $exception Exception to be thrown into the generator.
      * @param mixed $value Value to be sent into the generator.
      */
     $this->when = function ($exception, $value) {
         if ($this->depth > self::MAX_CONTINUATION_DEPTH) {
             // Defer continuation to avoid blowing up call stack.
             Loop::defer(function () use($exception, $value) {
                 ($this->when)($exception, $value);
             });
             return;
         }
         try {
             if ($exception) {
                 // Throw exception at current execution point.
                 $yielded = $this->generator->throw($exception);
             } else {
                 // Send the new value and execute to next yield statement.
                 $yielded = $this->generator->send($value);
             }
             if ($yielded instanceof Promise) {
                 ++$this->depth;
                 $yielded->when($this->when);
                 --$this->depth;
                 return;
             }
             if ($this->generator->valid()) {
                 $got = \is_object($yielded) ? \get_class($yielded) : \gettype($yielded);
                 throw new InvalidYieldError($this->generator, \sprintf("Unexpected yield (%s expected, got %s)", Promise::class, $got));
             }
             $this->resolve($this->generator->getReturn());
         } catch (\Throwable $exception) {
             $this->dispose($exception);
         }
     };
     try {
         $yielded = $this->generator->current();
         if ($yielded instanceof Promise) {
             ++$this->depth;
             $yielded->when($this->when);
             --$this->depth;
             return;
         }
         if ($this->generator->valid()) {
             $got = \is_object($yielded) ? \get_class($yielded) : \gettype($yielded);
             throw new InvalidYieldError($this->generator, \sprintf("Unexpected yield (%s expected, got %s)", Promise::class, $got));
         }
         $this->resolve($this->generator->getReturn());
     } catch (\Throwable $exception) {
         $this->dispose($exception);
     }
 }
Example #3
0
 /**
  * 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();
 }
Example #4
0
 /**
  * @param mixed $value
  *
  * @throws \Error Thrown if the promise has already been resolved.
  */
 private function resolve($value = null)
 {
     if ($this->resolved) {
         throw new \Error("Promise has already been resolved");
     }
     $this->resolved = true;
     $this->result = $value;
     if ($this->onResolved === null) {
         return;
     }
     $onResolved = $this->onResolved;
     $this->onResolved = null;
     if ($this->result instanceof Promise) {
         $this->result->when($onResolved);
         return;
     }
     try {
         $onResolved(null, $this->result);
     } catch (\Throwable $exception) {
         Loop::defer(static function () use($exception) {
             throw $exception;
         });
     }
 }
Example #5
0
/**
 * Defer the execution of a callback.
 * Returned Generators are run as coroutines. Failures of the coroutine are forwarded to the loop error handler.
 * 
 * @see \Interop\Async\Loop::defer()
 *
 * @param callable(string $watcherId, mixed $data) $callback The callback to delay.
 * @param mixed $data
 *
 * @return string Watcher identifier.
 */
function defer(callable $callback, $data = null) : string
{
    return Loop::defer(function ($watcherId, $data) use($callback) {
        $result = $callback($watcherId, $data);
        if ($result instanceof \Generator) {
            rethrow(new Coroutine($result));
        }
    }, $data);
}