guzzle / promises

Promises/A+ library for PHP with synchronous support
MIT License
7.61k stars 116 forks source link

Promise not working as async without wait() #89

Open zelin opened 6 years ago

zelin commented 6 years ago

The code works if i do $promise->wait();, however i am not sure how can i call the promise to work async.

foreach ($entries as $key=>$value)
        {     
                $promise = new Promise(function() use (&$promise, $key, $value) 
                {   
                    // Do some heavy lifting work.
                    $mainOutput = array($key, $output, $value);
                    $promise->resolve($mainOutput);
                });

                $promise->then(
                    // $onFulfilled
                    function ($mainOutput)
                    {
                        static::addToResponse($mainOutput);
                    },
                    // $onRejected
                    function ($reason) 
                    {
                        echo 'The promise was rejected.';
                    }
                );
            if(static::$sync)  
            {    
                $promise->wait();
            }
            else
            {

            }
        }
dignat commented 6 years ago

I am having the same issue. Any solutions, yet?

mcglonelevi commented 6 years ago

From what I can tell, you are trying to call multiple promises at once and wait for them to return? If this is the case, there is an all function here:

https://github.com/guzzle/promises/blob/master/src/functions.php

/**
 * Given an array of promises, return a promise that is fulfilled when all the
 * items in the array are fulfilled.
 *
 * The promise's fulfillment value is an array with fulfillment values at
 * respective positions to the original array. If any promise in the array
 * rejects, the returned promise is rejected with the rejection reason.
 *
 * @param mixed $promises Promises or values.
 * @param bool $recursive - If true, resolves new promises that might have been added to the stack during its own resolution.
 *
 * @return PromiseInterface
 */
TheTechsTech commented 5 years ago

I been working on just this problem intermixed with https://github.com/guzzle/promises/issues/44#issuecomment-320945095 and https://github.com/reactphp/promise/issues/66.

According to https://github.com/promises-aplus/constructor-spec/issues/18 the constructor implemented here is invalid. ReactPHP is valid to those spec's, but it will fail at runtime on your code example, and most Guzzle promise tests that tries to access an promise reference object of itself, it will produce an runtime error, access member method on null.

In order for your promise to work async, something like the following needs to be added to the constructor:

$promiseFunction = function () use($waitFn) {
    if (is_callable($waitFn)) {
        $waitFn([$this, 'resolve'], [$this, 'reject']);
    }
};

try {
    $promiseFunction();
} catch (\Throwable $e) {
    queue()->add($promiseFunction);
} catch (\Exception $exception) {
    queue()->add($promiseFunction);
}

This will end up producing an valid constructor according to spec's. It basically traps the otherwise error, and adds the callback to the event queue. However, it solves and introduces a few problems and wait method will need refactoring.

My guzzle/promises fork https://github.com/techno-express/promises/tree/interoperable, shows the problems solved and new errors created. The errors revealed should have not been produced, I applied similar to ReactPHP.

In my reactphp/promise fork https://github.com/techno-express/promise/tree/interoperable, I 've added an wait method to the library and it will pass most Guzzle wait implemented promise tests afterwards. It's simple as adding run() to the call, then check results.

For more background, the process I took started on another promise library in Sabre.io https://github.com/sabre-io/event/pull/66, which is similar to ReactPHP implementations, but had an wait method not constructed like Guzzle here. Since ReactPHP have no real promise tests or examples, https://github.com/reactphp/promise/issues/103, https://github.com/reactphp/promise/issues/127, https://github.com/reactphp/promise/issues/91, just a lot of mock tests. I used Sabre.io, and Guzzle promise tests, the ones it should pass, and ReactPHP fails some tests it shouldn't. There implementations should pass similar coded examples, if following JavaScript promise spec's.

I will submit PR, only if requested.

The other main change I made was to easily use any event loop implementation as https://github.com/reactphp/promise/issues/64. The wait method here is an forethought of the whole library to function, not an afterthought feature, that i find, can't be easily rewrote/refactored.