Skip to content

EachPromise should complete synchronously OR at least queue pumping its iterable upon creation #176

@mikethea1

Description

@mikethea1

PHP version: 8.2.12 (hint: php --version)

Description
If an EachPromise (or methods reliant of it like Each::of) is constructed with an empty iterable, it returns a promise in the pending state that only completes when waited on. In contrast, if the iterator has at least one item (even a fulfilled promise!) then it can be completed by running the queue.

It would be nice if things were more consistent. Either Each should always use the queue or it should complete synchronously if there is no async work to be done.

How to reproduce

// Empty case
$p = Each::of((function($b) { if ($b) { yield 1; } })(false));
$p->getState(); // "pending"
$p->then(function() { echo "DONE!"; });
\GuzzleHttp\Promise\Utils::queue()->run(); // does not print "DONE!"

// Non-empty case
$p = Each::of((function($b) { if ($b) { yield 1; } })(true));
$p->then(function() { echo "DONE!"; });
\GuzzleHttp\Promise\Utils::queue()->run(); // prints "DONE!"

Possible Solution
One solution would be to add the following code at the start of EachPromise::createPromise():

if (!$this->iterable->valid()) { return $this->aggregate = new FulfilledPromise(null); }

This way, it would complete synchronously with an empty iterator.

Additional context
The reason this is impacting me is that I'm using php fibers as a layer on top of Guzzle. When I want to wait on a promise, I need to pump the event loop (via CurlMultiHandler::tick()) to advance all promises until the current one is complete. This doesn't work if a promise becomes "orphaned" such that it is neither complete nor completable via the queue or the curl handler.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions