All Projects → reactphp → Promise Timer

reactphp / Promise Timer

Licence: mit
A trivial implementation of timeouts for Promises, built on top of ReactPHP.

Projects that are alternatives of or similar to Promise Timer

reactphp-child-process-promise
No description or website provided.
Stars: ✭ 12 (-95.56%)
Mutual labels:  promise, reactphp
Promise
Promises/A implementation for PHP.
Stars: ✭ 2,078 (+669.63%)
Mutual labels:  promise, reactphp
resloader
🎉A image preloaded plugin and can display the loaded image progress bar
Stars: ✭ 20 (-92.59%)
Mutual labels:  promise
Vue Modal Dialogs
Promisify dialogs in Vue!
Stars: ✭ 259 (-4.07%)
Mutual labels:  promise
asynqro
Futures and thread pool for C++ (with optional Qt support)
Stars: ✭ 103 (-61.85%)
Mutual labels:  promise
Web-Time-Tracker
Plugin named Timetracker is a time counter that works in both increase and decrease directions.
Stars: ✭ 21 (-92.22%)
Mutual labels:  timer
wxapp-api-interceptors
微信小程序api拦截器
Stars: ✭ 99 (-63.33%)
Mutual labels:  promise
bs-promise-monad
Monadic syntax to work with promise in ReasonML
Stars: ✭ 38 (-85.93%)
Mutual labels:  promise
React Loads
React Loads is a backend agnostic library to help with external data fetching & caching in your UI components.
Stars: ✭ 268 (-0.74%)
Mutual labels:  promise
new-clock
The best clock app there is
Stars: ✭ 24 (-91.11%)
Mutual labels:  timer
Simple Clock
Combination of a beautiful clock with widget, alarm, stopwatch & timer, no ads
Stars: ✭ 257 (-4.81%)
Mutual labels:  timer
node-split-file
🌱 NodeJS Module to split and merge files for several purposes like transporting over unstable networks.
Stars: ✭ 33 (-87.78%)
Mutual labels:  promise
tish
A replacement of shell script with TypeScript, for those who love TypeScript and tired of writing shell script, aiming to emulate shell script in TypeScript.
Stars: ✭ 119 (-55.93%)
Mutual labels:  promise
OnlyT
Meeting Timer
Stars: ✭ 80 (-70.37%)
Mutual labels:  timer
advisory-lock
Distributed locking using PostgreSQL advisory locks (Node.js)
Stars: ✭ 45 (-83.33%)
Mutual labels:  promise
Creed
Sophisticated and functionally-minded async with advanced features: coroutines, promises, ES2015 iterables, fantasy-land
Stars: ✭ 265 (-1.85%)
Mutual labels:  promise
Promises
It's just another Promise library...
Stars: ✭ 43 (-84.07%)
Mutual labels:  promise
run exclusive
⚡🔒 Wait queue for function execution 🔒 ⚡
Stars: ✭ 22 (-91.85%)
Mutual labels:  promise
tall
Promise-based, No-dependency URL unshortner (expander) module for Node.js
Stars: ✭ 56 (-79.26%)
Mutual labels:  promise
Snaptimer
Implementation of Snapchat's stories timer.
Stars: ✭ 273 (+1.11%)
Mutual labels:  timer

PromiseTimer

CI status

A trivial implementation of timeouts for Promises, built on top of ReactPHP.

Table of contents

Usage

This lightweight library consists only of a few simple functions. All functions reside under the React\Promise\Timer namespace.

The below examples assume you use an import statement similar to this:

use React\Promise\Timer;

Timer\timeout();

Alternatively, you can also refer to them with their fully-qualified name:

\React\Promise\Timer\timeout();

timeout()

The timeout(PromiseInterface $promise, $time, LoopInterface $loop) function can be used to cancel operations that take too long. You need to pass in an input $promise that represents a pending operation and timeout parameters. It returns a new Promise with the following resolution behavior:

  • If the input $promise resolves before $time seconds, resolve the resulting promise with its fulfillment value.
  • If the input $promise rejects before $time seconds, reject the resulting promise with its rejection value.
  • If the input $promise does not settle before $time seconds, cancel the operation and reject the resulting promise with a TimeoutException.

Internally, the given $time value will be used to start a timer that will cancel the pending operation once it triggers. This implies that if you pass a really small (or negative) value, it will still start a timer and will thus trigger at the earliest possible time in the future.

If the input $promise is already settled, then the resulting promise will resolve or reject immediately without starting a timer at all.

A common use case for handling only resolved values looks like this:

$promise = accessSomeRemoteResource();
Timer\timeout($promise, 10.0, $loop)->then(function ($value) {
    // the operation finished within 10.0 seconds
});

A more complete example could look like this:

$promise = accessSomeRemoteResource();
Timer\timeout($promise, 10.0, $loop)->then(
    function ($value) {
        // the operation finished within 10.0 seconds
    },
    function ($error) {
        if ($error instanceof Timer\TimeoutException) {
            // the operation has failed due to a timeout
        } else {
            // the input operation has failed due to some other error
        }
    }
);

Or if you're using react/promise v2.2.0 or up:

Timer\timeout($promise, 10.0, $loop)
    ->then(function ($value) {
        // the operation finished within 10.0 seconds
    })
    ->otherwise(function (Timer\TimeoutException $error) {
        // the operation has failed due to a timeout
    })
    ->otherwise(function ($error) {
        // the input operation has failed due to some other error
    })
;

Timeout cancellation

As discussed above, the timeout() function will cancel the underlying operation if it takes too long. This means that you can be sure the resulting promise will then be rejected with a TimeoutException.

However, what happens to the underlying input $promise is a bit more tricky: Once the timer fires, we will try to call $promise->cancel() on the input $promise which in turn invokes its cancellation handler.

This means that it's actually up the input $promise to handle cancellation support.

  • A common use case involves cleaning up any resources like open network sockets or file handles or terminating external processes or timers.

  • If the given input $promise does not support cancellation, then this is a NO-OP. This means that while the resulting promise will still be rejected, the underlying input $promise may still be pending and can hence continue consuming resources.

See the following chapter for more details on the cancellation handler.

Cancellation handler

For example, an implementation for the above operation could look like this:

function accessSomeRemoteResource()
{
    return new Promise(
        function ($resolve, $reject) use (&$socket) {
            // this will be called once the promise is created
            // a common use case involves opening any resources and eventually resolving
            $socket = createSocket();
            $socket->on('data', function ($data) use ($resolve) {
                $resolve($data);
            });
        },
        function ($resolve, $reject) use (&$socket) {
            // this will be called once calling `cancel()` on this promise
            // a common use case involves cleaning any resources and then rejecting
            $socket->close();
            $reject(new \RuntimeException('Operation cancelled'));
        }
    );
}

In this example, calling $promise->cancel() will invoke the registered cancellation handler which then closes the network socket and rejects the Promise instance.

If no cancellation handler is passed to the Promise constructor, then invoking its cancel() method it is effectively a NO-OP. This means that it may still be pending and can hence continue consuming resources.

For more details on the promise cancellation, please refer to the Promise documentation.

Input cancellation

Irrespective of the timeout handling, you can also explicitly cancel() the input $promise at any time. This means that the timeout() handling does not affect cancellation of the input $promise, as demonstrated in the following example:

$promise = accessSomeRemoteResource();
$timeout = Timer\timeout($promise, 10.0, $loop);

$promise->cancel();

The registered cancellation handler is responsible for handling the cancel() call:

  • A described above, a common use involves resource cleanup and will then reject the Promise. If the input $promise is being rejected, then the timeout will be aborted and the resulting promise will also be rejected.
  • If the input $promise is still pending, then the timout will continue running until the timer expires. The same happens if the input $promise does not register a cancellation handler.

Output cancellation

Similarily, you can also explicitly cancel() the resulting promise like this:

$promise = accessSomeRemoteResource();
$timeout = Timer\timeout($promise, 10.0, $loop);

$timeout->cancel();

Note how this looks very similar to the above input cancellation example. Accordingly, it also behaves very similar.

Calling cancel() on the resulting promise will merely try to cancel() the input $promise. This means that we do not take over responsibility of the outcome and it's entirely up to the input $promise to handle cancellation support.

The registered cancellation handler is responsible for handling the cancel() call:

  • As described above, a common use involves resource cleanup and will then reject the Promise. If the input $promise is being rejected, then the timeout will be aborted and the resulting promise will also be rejected.
  • If the input $promise is still pending, then the timout will continue running until the timer expires. The same happens if the input $promise does not register a cancellation handler.

To re-iterate, note that calling cancel() on the resulting promise will merely try to cancel the input $promise only. It is then up to the cancellation handler of the input promise to settle the promise. If the input promise is still pending when the timeout occurs, then the normal timeout cancellation handling will trigger, effectively rejecting the output promise with a TimeoutException.

This is done for consistency with the timeout cancellation handling and also because it is assumed this is often used like this:

$timeout = Timer\timeout(accessSomeRemoteResource(), 10.0, $loop);

$timeout->cancel();

As described above, this example works as expected and cleans up any resources allocated for the input $promise.

Note that if the given input $promise does not support cancellation, then this is a NO-OP. This means that while the resulting promise will still be rejected after the timeout, the underlying input $promise may still be pending and can hence continue consuming resources.

Collections

If you want to wait for multiple promises to resolve, you can use the normal promise primitives like this:

$promises = array(
    accessSomeRemoteResource(),
    accessSomeRemoteResource(),
    accessSomeRemoteResource()
);

$promise = \React\Promise\all($promises);

Timer\timeout($promise, 10, $loop)->then(function ($values) {
    // *all* promises resolved
});

The applies to all promise collection primitives alike, i.e. all(), race(), any(), some() etc.

For more details on the promise primitives, please refer to the Promise documentation.

resolve()

The resolve($time, LoopInterface $loop) function can be used to create a new Promise that resolves in $time seconds with the $time as the fulfillment value.

Timer\resolve(1.5, $loop)->then(function ($time) {
    echo 'Thanks for waiting ' . $time . ' seconds' . PHP_EOL;
});

Internally, the given $time value will be used to start a timer that will resolve the promise once it triggers. This implies that if you pass a really small (or negative) value, it will still start a timer and will thus trigger at the earliest possible time in the future.

Resolve cancellation

You can explicitly cancel() the resulting timer promise at any time:

$timer = Timer\resolve(2.0, $loop);

$timer->cancel();

This will abort the timer and reject with a RuntimeException.

reject()

The reject($time, LoopInterface $loop) function can be used to create a new Promise which rejects in $time seconds with a TimeoutException.

Timer\reject(2.0, $loop)->then(null, function (TimeoutException $e) {
    echo 'Rejected after ' . $e->getTimeout() . ' seconds ' . PHP_EOL;
});

Internally, the given $time value will be used to start a timer that will reject the promise once it triggers. This implies that if you pass a really small (or negative) value, it will still start a timer and will thus trigger at the earliest possible time in the future.

This function complements the resolve() function and can be used as a basic building block for higher-level promise consumers.

Reject cancellation

You can explicitly cancel() the resulting timer promise at any time:

$timer = Timer\reject(2.0, $loop);

$timer->cancel();

This will abort the timer and reject with a RuntimeException.

TimeoutException

The TimeoutException extends PHP's built-in RuntimeException.

The getTimeout() method can be used to get the timeout value in seconds.

Install

The recommended way to install this library is through Composer. New to Composer?

This project follows SemVer. This will install the latest supported version:

$ composer require react/promise-timer:^1.6

See also the CHANGELOG for details about version upgrades.

This project aims to run on any platform and thus does not require any PHP extensions and supports running on legacy PHP 5.3 through current PHP 8+ and HHVM. It's highly recommended to use PHP 7+ for this project.

Tests

To run the test suite, you first need to clone this repo and then install all dependencies through Composer:

$ composer install

To run the test suite, go to the project root and run:

$ php vendor/bin/phpunit

License

MIT, see LICENSE file.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].