Promises/A+ library for PHP with synchronous support

Related tags

Promises promises
Overview

Guzzle Promises

Promises/A+ implementation that handles promise chaining and resolution iteratively, allowing for "infinite" promise chaining while keeping the stack size constant. Read this blog post for a general introduction to promises.

Features

  • Promises/A+ implementation.
  • Promise resolution and chaining is handled iteratively, allowing for "infinite" promise chaining.
  • Promises have a synchronous wait method.
  • Promises can be cancelled.
  • Works with any object that has a then function.
  • C# style async/await coroutine promises using GuzzleHttp\Promise\Coroutine::of().

Quick start

A promise represents the eventual result of an asynchronous operation. The primary way of interacting with a promise is through its then method, which registers callbacks to receive either a promise's eventual value or the reason why the promise cannot be fulfilled.

Callbacks

Callbacks are registered with the then method by providing an optional $onFulfilled followed by an optional $onRejected function.

use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise->then(
    // $onFulfilled
    function ($value) {
        echo 'The promise was fulfilled.';
    },
    // $onRejected
    function ($reason) {
        echo 'The promise was rejected.';
    }
);

Resolving a promise means that you either fulfill a promise with a value or reject a promise with a reason. Resolving a promises triggers callbacks registered with the promises's then method. These callbacks are triggered only once and in the order in which they were added.

Resolving a promise

Promises are fulfilled using the resolve($value) method. Resolving a promise with any value other than a GuzzleHttp\Promise\RejectedPromise will trigger all of the onFulfilled callbacks (resolving a promise with a rejected promise will reject the promise and trigger the $onRejected callbacks).

use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise
    ->then(function ($value) {
        // Return a value and don't break the chain
        return "Hello, " . $value;
    })
    // This then is executed after the first then and receives the value
    // returned from the first then.
    ->then(function ($value) {
        echo $value;
    });

// Resolving the promise triggers the $onFulfilled callbacks and outputs
// "Hello, reader."
$promise->resolve('reader.');

Promise forwarding

Promises can be chained one after the other. Each then in the chain is a new promise. The return value of a promise is what's forwarded to the next promise in the chain. Returning a promise in a then callback will cause the subsequent promises in the chain to only be fulfilled when the returned promise has been fulfilled. The next promise in the chain will be invoked with the resolved value of the promise.

use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$nextPromise = new Promise();

$promise
    ->then(function ($value) use ($nextPromise) {
        echo $value;
        return $nextPromise;
    })
    ->then(function ($value) {
        echo $value;
    });

// Triggers the first callback and outputs "A"
$promise->resolve('A');
// Triggers the second callback and outputs "B"
$nextPromise->resolve('B');

Promise rejection

When a promise is rejected, the $onRejected callbacks are invoked with the rejection reason.

use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise->then(null, function ($reason) {
    echo $reason;
});

$promise->reject('Error!');
// Outputs "Error!"

Rejection forwarding

If an exception is thrown in an $onRejected callback, subsequent $onRejected callbacks are invoked with the thrown exception as the reason.

use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise->then(null, function ($reason) {
    throw new Exception($reason);
})->then(null, function ($reason) {
    assert($reason->getMessage() === 'Error!');
});

$promise->reject('Error!');

You can also forward a rejection down the promise chain by returning a GuzzleHttp\Promise\RejectedPromise in either an $onFulfilled or $onRejected callback.

use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\RejectedPromise;

$promise = new Promise();
$promise->then(null, function ($reason) {
    return new RejectedPromise($reason);
})->then(null, function ($reason) {
    assert($reason === 'Error!');
});

$promise->reject('Error!');

If an exception is not thrown in a $onRejected callback and the callback does not return a rejected promise, downstream $onFulfilled callbacks are invoked using the value returned from the $onRejected callback.

use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise
    ->then(null, function ($reason) {
        return "It's ok";
    })
    ->then(function ($value) {
        assert($value === "It's ok");
    });

$promise->reject('Error!');

Synchronous wait

You can synchronously force promises to complete using a promise's wait method. When creating a promise, you can provide a wait function that is used to synchronously force a promise to complete. When a wait function is invoked it is expected to deliver a value to the promise or reject the promise. If the wait function does not deliver a value, then an exception is thrown. The wait function provided to a promise constructor is invoked when the wait function of the promise is called.

$promise = new Promise(function () use (&$promise) {
    $promise->resolve('foo');
});

// Calling wait will return the value of the promise.
echo $promise->wait(); // outputs "foo"

If an exception is encountered while invoking the wait function of a promise, the promise is rejected with the exception and the exception is thrown.

$promise = new Promise(function () use (&$promise) {
    throw new Exception('foo');
});

$promise->wait(); // throws the exception.

Calling wait on a promise that has been fulfilled will not trigger the wait function. It will simply return the previously resolved value.

$promise = new Promise(function () { die('this is not called!'); });
$promise->resolve('foo');
echo $promise->wait(); // outputs "foo"

Calling wait on a promise that has been rejected will throw an exception. If the rejection reason is an instance of \Exception the reason is thrown. Otherwise, a GuzzleHttp\Promise\RejectionException is thrown and the reason can be obtained by calling the getReason method of the exception.

$promise = new Promise();
$promise->reject('foo');
$promise->wait();

PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'

Unwrapping a promise

When synchronously waiting on a promise, you are joining the state of the promise into the current state of execution (i.e., return the value of the promise if it was fulfilled or throw an exception if it was rejected). This is called "unwrapping" the promise. Waiting on a promise will by default unwrap the promise state.

You can force a promise to resolve and not unwrap the state of the promise by passing false to the first argument of the wait function:

$promise = new Promise();
$promise->reject('foo');
// This will not throw an exception. It simply ensures the promise has
// been resolved.
$promise->wait(false);

When unwrapping a promise, the resolved value of the promise will be waited upon until the unwrapped value is not a promise. This means that if you resolve promise A with a promise B and unwrap promise A, the value returned by the wait function will be the value delivered to promise B.

Note: when you do not unwrap the promise, no value is returned.

Cancellation

You can cancel a promise that has not yet been fulfilled using the cancel() method of a promise. When creating a promise you can provide an optional cancel function that when invoked cancels the action of computing a resolution of the promise.

API

Promise

When creating a promise object, you can provide an optional $waitFn and $cancelFn. $waitFn is a function that is invoked with no arguments and is expected to resolve the promise. $cancelFn is a function with no arguments that is expected to cancel the computation of a promise. It is invoked when the cancel() method of a promise is called.

use GuzzleHttp\Promise\Promise;

$promise = new Promise(
    function () use (&$promise) {
        $promise->resolve('waited');
    },
    function () {
        // do something that will cancel the promise computation (e.g., close
        // a socket, cancel a database query, etc...)
    }
);

assert('waited' === $promise->wait());

A promise has the following methods:

  • then(callable $onFulfilled, callable $onRejected) : PromiseInterface

    Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler.

  • otherwise(callable $onRejected) : PromiseInterface

    Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled.

  • wait($unwrap = true) : mixed

    Synchronously waits on the promise to complete.

    $unwrap controls whether or not the value of the promise is returned for a fulfilled promise or if an exception is thrown if the promise is rejected. This is set to true by default.

  • cancel()

    Attempts to cancel the promise if possible. The promise being cancelled and the parent most ancestor that has not yet been resolved will also be cancelled. Any promises waiting on the cancelled promise to resolve will also be cancelled.

  • getState() : string

    Returns the state of the promise. One of pending, fulfilled, or rejected.

  • resolve($value)

    Fulfills the promise with the given $value.

  • reject($reason)

    Rejects the promise with the given $reason.

FulfilledPromise

A fulfilled promise can be created to represent a promise that has been fulfilled.

use GuzzleHttp\Promise\FulfilledPromise;

$promise = new FulfilledPromise('value');

// Fulfilled callbacks are immediately invoked.
$promise->then(function ($value) {
    echo $value;
});

RejectedPromise

A rejected promise can be created to represent a promise that has been rejected.

use GuzzleHttp\Promise\RejectedPromise;

$promise = new RejectedPromise('Error');

// Rejected callbacks are immediately invoked.
$promise->then(null, function ($reason) {
    echo $reason;
});

Promise interop

This library works with foreign promises that have a then method. This means you can use Guzzle promises with React promises for example. When a foreign promise is returned inside of a then method callback, promise resolution will occur recursively.

// Create a React promise
$deferred = new React\Promise\Deferred();
$reactPromise = $deferred->promise();

// Create a Guzzle promise that is fulfilled with a React promise.
$guzzlePromise = new GuzzleHttp\Promise\Promise();
$guzzlePromise->then(function ($value) use ($reactPromise) {
    // Do something something with the value...
    // Return the React promise
    return $reactPromise;
});

Please note that wait and cancel chaining is no longer possible when forwarding a foreign promise. You will need to wrap a third-party promise with a Guzzle promise in order to utilize wait and cancel functions with foreign promises.

Event Loop Integration

In order to keep the stack size constant, Guzzle promises are resolved asynchronously using a task queue. When waiting on promises synchronously, the task queue will be automatically run to ensure that the blocking promise and any forwarded promises are resolved. When using promises asynchronously in an event loop, you will need to run the task queue on each tick of the loop. If you do not run the task queue, then promises will not be resolved.

You can run the task queue using the run() method of the global task queue instance.

// Get the global task queue
$queue = GuzzleHttp\Promise\Utils::queue();
$queue->run();

For example, you could use Guzzle promises with React using a periodic timer:

$loop = React\EventLoop\Factory::create();
$loop->addPeriodicTimer(0, [$queue, 'run']);

TODO: Perhaps adding a futureTick() on each tick would be faster?

Implementation notes

Promise resolution and chaining is handled iteratively

By shuffling pending handlers from one owner to another, promises are resolved iteratively, allowing for "infinite" then chaining.

<?php
require 'vendor/autoload.php';

use GuzzleHttp\Promise\Promise;

$parent = new Promise();
$p = $parent;

for ($i = 0; $i < 1000; $i++) {
    $p = $p->then(function ($v) {
        // The stack size remains constant (a good thing)
        echo xdebug_get_stack_depth() . ', ';
        return $v + 1;
    });
}

$parent->resolve(0);
var_dump($p->wait()); // int(1000)

When a promise is fulfilled or rejected with a non-promise value, the promise then takes ownership of the handlers of each child promise and delivers values down the chain without using recursion.

When a promise is resolved with another promise, the original promise transfers all of its pending handlers to the new promise. When the new promise is eventually resolved, all of the pending handlers are delivered the forwarded value.

A promise is the deferred.

Some promise libraries implement promises using a deferred object to represent a computation and a promise object to represent the delivery of the result of the computation. This is a nice separation of computation and delivery because consumers of the promise cannot modify the value that will be eventually delivered.

One side effect of being able to implement promise resolution and chaining iteratively is that you need to be able for one promise to reach into the state of another promise to shuffle around ownership of handlers. In order to achieve this without making the handlers of a promise publicly mutable, a promise is also the deferred value, allowing promises of the same parent class to reach into and modify the private properties of promises of the same type. While this does allow consumers of the value to modify the resolution or rejection of the deferred, it is a small price to pay for keeping the stack size constant.

$promise = new Promise();
$promise->then(function ($value) { echo $value; });
// The promise is the deferred value, so you can deliver a value to it.
$promise->resolve('foo');
// prints "foo"

Upgrading from Function API

A static API was first introduced in 1.4.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API will be removed in 2.0.0. A migration table has been provided here for your convenience:

Original Function Replacement Method
queue Utils::queue
task Utils::task
promise_for Create::promiseFor
rejection_for Create::rejectionFor
exception_for Create::exceptionFor
iter_for Create::iterFor
inspect Utils::inspect
inspect_all Utils::inspectAll
unwrap Utils::unwrap
all Utils::all
some Utils::some
any Utils::any
settle Utils::settle
each Each::of
each_limit Each::ofLimit
each_limit_all Each::ofLimitAll
!is_fulfilled Is::pending
is_fulfilled Is::fulfilled
is_rejected Is::rejected
is_settled Is::settled
coroutine Coroutine::of
Comments
  • <b>Fatal error</b>:  Interface 'GuzzleHttp\Promise\TaskQueueInterface' not found in

    Fatal error: Interface 'GuzzleHttp\Promise\TaskQueueInterface' not found in

    Fatal error: Interface 'GuzzleHttp\Promise\TaskQueueInterface' not found in vendor/guzzlehttp/promises/src/TaskQueue.php on line

    13

    Can someone explain how this can be solved ?

    opened by mirzazeyrek 17
  • Fix #57

    Fix #57

    A freshly created promise can now be injected into its wait function for simpler creation / resolution:

    use GuzzleHttp\Promise\Promise;
    use GuzzleHttp\Promise\PromiseInterface;
    
    $promise = new Promise(function (PromiseInterface $promise) {
        $promise->resolve('The promise has been resolved');
    });
    
    opened by bpolaszek 14
  • Can I manipulate the response body on fulfilled promise with a callback?

    Can I manipulate the response body on fulfilled promise with a callback?

    here's my example:

    // Build Asynchronous Promise
    $promise = $this->client->requestAsync($this->method, $path, $options);
    
    // Extract target data from response JSON after request fulfilled
    $promise->then(
        function ($response) {
            $result = GuzzleResponseParser::parse($response);
            $extracted = $this->getTargetDataFromResult($result);
    
            //This dumps the right data
            //var_dump($extracted);
            //exit();
    
            // This, however, does not work as I'd expect.  It doesn't error, but see notes below.                 
            return $extracted;
        }
    );
    

    I am later using

    $results = Promise\unwrap($promises);
    foreach ($results as $key => $result) {
        $this->data[$key] = $result;
    }
    

    I was hoping the $extracted in the then() closure would translate to the $result in the post-unwrap loop, but this does not happen. I see the regular result, not the extracted result.

    Am I going about this wrong? I've reviewed documentation and can't seem to find what I need. Might the be another way to do this, or is it not possible? Thanks in advance!

    question 
    opened by arderyp 10
  • Fix coroutine memory leak

    Fix coroutine memory leak

    Picking up #35 where @joshdifabio left off. Running through the code sample provided in that PR without this change on PHP7 causes memory usage to grow until PHP segfaults. With this change, memory holds at 6MB while running the entire coroutine.

    One potential issue with this approach is the use of wait.

    opened by jeskew 10
  • each_limit skipping promises and failing with

    each_limit skipping promises and failing with "Invoking the wait callback did not resolve the promise"

    PHP version: 7.4.9

    Description I have some code that downloads and processes sequences of pages multiple times. Multiple instances of these downloads are gathered up and passed to each_limit to process the chains at a limited concurrency.

    With 1.3.1 this process works overall. After upgrading to 1.4.0 the script fails with

    Fatal error: Uncaught GuzzleHttp\Promise\RejectionException: The promise was rejected with reason: Invoking the wait callback did not resolve the promise
    

    The script also appears to skip some of the promises.

    How to reproduce I've reproduced the issue with the following example code:

    <?php
    require 'vendor/autoload.php';
    
    use GuzzleHttp\Promise\Promise;
    use function GuzzleHttp\Promise\each_limit;
    
    class Downloader {
        public function download($upper){
    	return $this->step1($upper)
    	    ->then(function($result){
    		return $this->step2($result);
    	    })->then(function($result){
    		return $this->step3($result);
    	    })->then(function($result){
    		return $this->step4($result);
    	    });
        }
    
        private function step1($upper){
    	$upper = $upper & 0xFF00;
    
    	return $promise = new Promise(function() use (&$promise, $upper){
    	    $promise->resolve($upper);
    	});
        }
    
        private function step2($result){
    	$result = $result | 0x02;
    
    	return $promise = new Promise(function() use (&$promise, $result){
    	    $promise->resolve($result);
    	});
        }
    
        private function step3($result){
    	$result = $result | 0x04;
    
    	return $promise = new Promise(function() use (&$promise, $result){
    	    $promise->resolve($result);
    	});
        }
    
        private function step4($result){
    	return $result | 0x08;
        }
    }
    
    
    function generateDownloadPromises(Downloader $dl, $entries){
        foreach ($entries as $item){
    	yield $dl->download($item << 8)->then(function($result){
    	    echo 'Received value: ' . $result . PHP_EOL;
    
    	    return $result;
    	});
        }
    }
    
    $dl = new Downloader();
    $generator = generateDownloadPromises($dl, range(1, 10));
    each_limit($generator, 3, function($result, $key){
        echo '[' . $key . '] Promise resolved successfully to ' . $result, PHP_EOL;
    }, function($reason, $key){
        echo '[' . $key . '] Failed to resolve promise', PHP_EOL;
        var_dump($reason);
    })->wait();
    

    Results Running the above on 1.3.1 yields the output:

    Received value: 270
    [0] Promise resolved successfully to 270
    Received value: 526
    [1] Promise resolved successfully to 526
    Received value: 782
    [2] Promise resolved successfully to 782
    Received value: 1038
    [3] Promise resolved successfully to 1038
    Received value: 1294
    [4] Promise resolved successfully to 1294
    Received value: 1550
    [5] Promise resolved successfully to 1550
    Received value: 1806
    [6] Promise resolved successfully to 1806
    Received value: 2062
    [7] Promise resolved successfully to 2062
    Received value: 2318
    [8] Promise resolved successfully to 2318
    Received value: 2574
    [9] Promise resolved successfully to 2574
    

    While on 1.4.0 it yields:

    Received value: 270
    [0] Promise resolved successfully to 270
    Received value: 1038
    [3] Promise resolved successfully to 1038
    Received value: 1294
    [4] Promise resolved successfully to 1294
    Received value: 1550
    [5] Promise resolved successfully to 1550
    Received value: 1806
    [6] Promise resolved successfully to 1806
    Received value: 2062
    [7] Promise resolved successfully to 2062
    Received value: 2318
    [8] Promise resolved successfully to 2318
    Received value: 2574
    [9] Promise resolved successfully to 2574
    PHP Fatal error:  Uncaught GuzzleHttp\Promise\RejectionException: The promise was rejected with reason: Invoking the wait callback did not resolve the promise in G:\xxx\vendor\guzzlehttp\promises\src\Create.php:62
    Stack trace:
    #0 G:\xxx\vendor\guzzlehttp\promises\src\Promise.php(72): GuzzleHttp\Promise\Create::exceptionFor('Invoking the wa...')
    #1 G:\xxx\each_bug.php(66): GuzzleHttp\Promise\Promise->wait()
    #2 {main}
      thrown in G:\xxx\vendor\guzzlehttp\promises\src\Create.php on line 62
    

    Additional context

    Aside from the unexpected exception in 1.4.0, you can see that the indexes [1] and [2] are missing, those promises are skipped and never processed.

    Looking through and testing various previous commits it appears this issue was introduced by commit 27c7a89. The code works when using the prior commit 956c5d1

    opened by kicken 9
  • Run queue when waiting on fulfilled promise

    Run queue when waiting on fulfilled promise

    Description

    This PR tries to fix an inconsistency when waiting for a FulfilledPromise.

    As a developer I expect that the $onFulfilled callback will be resolved as soon as I call wait() on any implementation of PromiseInterface.

    Right now, the then() function on a fulfilled promise returns a new promise, which has to be resolved. This seems pretty confusing to me, since this is different to how a normal promise is handled.

    How to reproduce

    See reproducer in https://github.com/jaylinski/guzzle-fulfilled-wait/blob/master/test.php.

    Possible Solution Merge this PR.

    Additional context This may break other assumptions. See https://github.com/guzzle/promises/issues/14.

    opened by jaylinski 9
  • Complementary functions with concurrency control

    Complementary functions with concurrency control

    We have some handy functions to work with promises, but they don't support concurrency control.

    This PR adds complementary functions with concurrent control (like each_limit()):

    • all_limit()
    • some_limit()
    • any_limit()
    • settle_limit()
    opened by alexeyshockov 9
  • Fix wait() foreign promise compatibility

    Fix wait() foreign promise compatibility

    Promise\wait() appears to be incompatible with other PromiseInterface implementations. This PR addresses the issue and includes a test which fails in v1.3.0 but which passes with the proposed changes. This PR should address #54 and aws/aws-sdk-php#1125.

    opened by joshdifabio 9
  • Make it possible to specify a new queue instance

    Make it possible to specify a new queue instance

    In order to enable efficient integrations with reactors, it would be very helpful to be able to provide a custom task queue.

    Here's a simple example of what would be possible after merging this PR:

    use Amp\Reactor;
    use GuzzleHttp\Promise\TaskQueue;
    
    class AmpTaskQueue extends TaskQueue
    {
        private $reactor;
    
        public function __construct(Reactor $reactor)
        {
            $this->reactor = $reactor;
        }
    
        public function add(callable $task)
        {
            if ($this->isEmpty()) {
                $this->reactor->immediately([$this, 'run']);
            }
    
            parent::add($task);
        }
    }
    
    \GuzzleHttp\Promise\queue(new AmpTaskQueue($reactor));
    

    Simply calling $reactor->immedately($task); would really be preferable but, unfortunately, this would prevent the run() and isEmpty() methods from working as expected.

    opened by joshdifabio 9
  • FulfilledPromise not invoking callbacks immediately

    FulfilledPromise not invoking callbacks immediately

    The comments for FulfilledPromise indicate that any calls to then() will invoke the callback immediately, but it seems to just add it to queue().

    Is the documentation wrong or is this a bug?

    opened by mikegreiling 9
  • Exceptions in nested handlers are not reported

    Exceptions in nested handlers are not reported

    $p = new Promise();
    $p->then(function () {
        $inner = new Promise();
        $inner->then(function () {
            throw new \RuntimeException('This exception should not be trapped');
        });
        $inner->resolve(true);
        $inner->wait();
    
        print_r(['state' => $inner->getState()]);
    
        return $inner;
    });
    $p->resolve(true);
    $p->wait();
    
    print_r(['state' => $inner->getState()]);
    

    In this scenario, the RuntimeException should not be trapped. Both promises are correctly fulfilled but the exception is never seen.

    Expected Result

    1. RuntimeException is thrown.

    Current Result

    1. RuntimeException is never seen.
    opened by shadowhand 9
  • Allow all promise collection wrappers to dynamically add promises via $recursive option

    Allow all promise collection wrappers to dynamically add promises via $recursive option

    (Proposed in #46, implemented in #63) Utils::all() has the $recursive option that makes the collection promise pick up new promises that have been dynamically added to the collection. The other collection methods (settle, any, some, ...) lack that $recursive option. They should also have it.

    My background

    I have a use case (recursive redirect resolver) where i rather use Utils::settle() (to not have a fail stop the other requests). Taking the existing code from ::all(), i could implement this in userland, so no urgency to me.

    opened by geek-merlin 0
  • Add $config to the signatures of Utils::all and Each::of ?

    Add $config to the signatures of Utils::all and Each::of ?

    Description Does it make sense to add $config as a third optional parameter to Utils::all, so that callers have more control over the underlying EachPromise (like concurrency)?

    Example Current code: (this is based on my understanding of Utils::all, unrolled so I can pass $config to EachPromise. If I'm doing this the hard way, this whole request could be moot)

    $everything = [];
    return (new EachPromise($requestPromises, [
        'concurrency' => 5,
        'fulfilled' => function ($oneResult) use (&$everything) {
            $everything = array_merge($everything, $oneResult);
        },
    ]))
        ->promise()
        ->then(function () use (&$everything) {
             return $everything;
        });
    

    Desired code:

    return Utils::all($requestPromises, false, ['concurrency' => 5])->then(function ($results)  {
        return array_merge(...$results);
    });
    

    Additional context I'd be happy to submit a PR for this, I mostly wanted some advice if this is even a good idea, in line with how you want the library to work.

    opened by jwadhams 0
  • State inconsistency with chained promises

    State inconsistency with chained promises

    In general, if a promise's getState() returns FULFILLED or REJECTED, then wait() returns a value or error immediately. (i.e., you can call inspect() on a non-pending promise and get a valid answer).

    But this is not the case if you resolve() or reject() with a promise -- either directly, or indirectly by returning one from a then() handler. In such a case, you can have a promise whose state is fulfilled or rejected on the surface but is actually pending internally. Thiis appears to be a violation of section 2.3.2.1 of the Promises/A+ spec, i.e. "If x is pending, promise must remain pending until x is fulfilled or rejected. But this library's promise implementation prematurely settles the promise, rather than leaving it pending per the spec.

    In cases where you want to be able to poll the value of promises (but can't actually wait on them), this is a problem because there is no way to know from outside the promise that this pseudo-fulfillment condition can be detected. The only way to avoid it seems to be to either 1) exclusively use coroutines, 2) never use getState() and wait(), or 3) never call resolve() with a promise or return one from a then() handler.

    ISTM that it would be better here to follow the spec, and have a promise remain pending when resolved or rejected with another promise, until such time as the other promise resolves or rejects, especially since a pending promise passed to resolve() could later reject, resulting in the paradoxical condition of a promise whose state is "fulfilled", yet throws an error when you wait() on it!

    opened by pjeby 0
  • Promise not working as async without wait()

    Promise not working as async without wait()

    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
                {
    
                }
            }
    
    opened by zelin 3
  • Event loop integration makes bad recommendation

    Event loop integration makes bad recommendation

    It suggests the following as event loop integration for ReactPHP:

    $loop = React\EventLoop\Factory::create();
    $loop->addPeriodicTimer(0, [$queue, 'run']);
    

    That will basically be an infinite loop that always calls [$queue, 'run'] and hogs the CPU until the entire thing is done. A better way would be a short timer like 10 milliseconds. It's not as responsive, but at least it doesn't consume 100% CPU.

    opened by kelunik 2
  • Producer -> consumer chain?

    Producer -> consumer chain?

    I don't really understand can I implement producer-consumer scheme using this library?

    For example, imagine a producing function which makes some values over time, and another consuming function which takes the values. Is there a way to implement this using Promises?

    opened by OnkelTem 1
Releases(1.5.2)
Owner
Guzzle
An extensible PHP HTTP client
Guzzle
The library resolves the address of the nearest server to you by domain name and Your IP address

PHP Domain DNS ?? The library resolves the address of the nearest server to you by domain name and Your IP address Installing $ composer require kayw-

Kay W. 49 May 12, 2021
A non-blocking concurrency framework for PHP applications. 🐘

Amp is a non-blocking concurrency framework for PHP. It provides an event loop, promises and streams as a base for asynchronous programming. Promises

Amp 3.8k Jan 4, 2023
Asynchronous coroutines for PHP 7.

Recoil An asynchronous coroutine kernel for PHP 7. composer require recoil/recoil The Recoil project comprises the following packages: recoil/api - T

Recoil 787 Dec 8, 2022
Icicle is a PHP library for writing asynchronous code using synchronous coding techniques

Icicle is now deprecated in favor of Amp v2.0. This version is is currently under development, but close to release. The v2.0 branches are amp_v2 in a

icicle.io 1.1k Dec 21, 2022
Promises/A implementation for PHP.

Promise A lightweight implementation of CommonJS Promises/A for PHP. The master branch contains the code for the upcoming 3.0 release. For the code of

ReactPHP 2.2k Jan 3, 2023
A minimalistic implementation of Promises for PHP

libPromise A minimalistic implementation of Promises for PHP. Installation via DEVirion Install the DEVirion plugin and start your server. This will c

null 8 Sep 27, 2022
PHPUnit assertions for testing ReactPHP promises

ReactPHP Promises Testing A library that provides a set of convenient assertions for testing ReactPHP promises. Under the hood uses clue/php-block-rea

Sergey Zhuk 30 Dec 8, 2022
Pico disk, Not need any database, support html5, support mp3, mp4, support streaming media, support AriaNg

Nano netdisk, Now changed to pico disk. Pico disk,does not need any database, support html5, support mp3, mp4, support streaming media, support AriaNg.

null 53 Dec 26, 2022
Michael Pratt 307 Dec 23, 2022
The Assure Alliance support website. This website is based on Questions2Answers and is a forum for support using Biblical Tools

The Assure Alliance support website. This website is based on Questions2Answers and is a forum for support using Biblical Tools

United Bible Societies Institute for Computer Assisted Publishing 3 Jul 29, 2022
This library extends the 'League OAuth2 Client' library to provide OpenID Connect Discovery support for supporting providers that expose a .well-known configuration endpoint.

OpenID Connect Discovery support for League - OAuth 2.0 Client This library extends the League OAuth2 Client library to provide OpenID Connect Discove

null 3 Jan 8, 2022
A PHP string manipulation library with multibyte support. Compatible with PHP 5.4+, PHP 7+, and HHVM.

A PHP string manipulation library with multibyte support. Compatible with PHP 5.4+, PHP 7+, and HHVM. s('string')->toTitleCase()->ensureRight('y') ==

Daniel St. Jules 2.5k Dec 28, 2022
A PHP library to support implementing representations for HATEOAS REST web services.

Hateoas A PHP library to support implementing representations for HATEOAS REST web services. Installation Working With Symfony Usage Introduction Conf

William Durand 998 Dec 5, 2022
The first PHP Library to support OAuth for Twitter's REST API.

THIS IS AN MODIFIED VERSION OF ABRAHAMS TWITTER OAUTH CLASS The directories are structured and the class uses PHP5.3 namespaces. Api.php has a new

Ruud Kamphuis 51 Feb 11, 2021
Lightweight and feature-rich PHP validation and filtering library. Support scene grouping, pre-filtering, array checking, custom validators, custom messages. 轻量且功能丰富的PHP验证、过滤库。支持场景分组,前置过滤,数组检查,自定义验证器,自定义消息。

PHP Validate 一个简洁小巧且功能完善的php验证、过滤库。 简单方便,支持添加自定义验证器 支持前置验证检查, 自定义如何判断非空 支持将规则按场景进行分组设置。或者部分验证 支持在进行验证前对值使用过滤器进行净化过滤内置过滤器 支持在进行验证前置处理和后置处理独立验证处理 支持自定义每

Inhere 246 Jan 5, 2023
A PHP string manipulation library with multibyte support

A PHP string manipulation library with multibyte support. Compatible with PHP 5.4+, PHP 7+, and HHVM. s('string')->toTitleCase()->ensureRight('y') ==

Daniel St. Jules 2.5k Jan 3, 2023
:accept: Stringy - A PHP string manipulation library with multibyte support, performance optimized

?? Stringy A PHP string manipulation library with multibyte support. Compatible with PHP 7+ 100% compatible with the original "Stringy" library, but t

Lars Moelleken 144 Dec 12, 2022
PHP library providing retry functionality with multiple backoff strategies and jitter support

PHP Backoff Easily wrap your code with retry functionality. This library provides: 4 backoff strategies (plus the ability to use your own) Optional ji

Signature Tech Studio 145 Dec 21, 2022
A GETTR.com client library written in PHP with Laravel support.

Gettr API Clinet PHP A GETTR.com client library written in PHP with Laravel support. This library uses unofficial publicly accessible API endpoints of

null 10 Dec 13, 2022
📧 Handy email creation and transfer library for PHP with both text and MIME-compliant support.

?? Handy email creation and transfer library for PHP with both text and MIME-compliant support.

Nette Foundation 401 Dec 22, 2022