Giter Club home page Giter Club logo

promise-timer's Introduction

PromiseTimer

CI status installs on Packagist

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 refer to all functions with their fully-qualified names like this:

React\Promise\Timer\timeout(…);

As of PHP 5.6+ you can also import each required function into your code like this:

use function React\Promise\Timer\timeout;

timeout(…);

Alternatively, you can also use an import statement similar to this:

use React\Promise\Timer;

Timer\timeout(…);

timeout()

The timeout(PromiseInterface<T> $promise, float $time, ?LoopInterface $loop = null): PromiseInterface<T> 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.

This function takes an optional LoopInterface|null $loop parameter that can be used to pass the event loop instance to use. You can use a null value here in order to use the default loop. This value SHOULD NOT be given unless you're sure you want to explicitly use a given event loop instance.

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

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

A more complete example could look like this:

$promise = accessSomeRemoteResource();
React\Promise\Timer\timeout($promise, 10.0)->then(
    function ($value) {
        // the operation finished within 10.0 seconds
    },
    function ($error) {
        if ($error instanceof React\Promise\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 v3:

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

As discussed above, the timeout() function will take care of the underlying operation if it takes too long. In this case, you can be sure the resulting promise will always be rejected with a TimeoutException. On top of this, the function will try to cancel the underlying operation. Responsibility for this cancellation logic is left up to the underlying operation.

  • 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

On top of this, the returned promise is implemented in such a way that it can be cancelled when it is still pending. Cancelling a pending promise will cancel the underlying operation. As discussed above, responsibility for this cancellation logic is left up to the underlying operation.

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

$timeout->cancel();

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

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);

React\Promise\Timer\timeout($promise, 10)->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.

sleep()

The sleep(float $time, ?LoopInterface $loop = null): PromiseInterface<void> function can be used to create a new promise that resolves in $time seconds.

React\Promise\Timer\sleep(1.5)->then(function () {
    echo 'Thanks for waiting!' . 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.

This function takes an optional LoopInterface|null $loop parameter that can be used to pass the event loop instance to use. You can use a null value here in order to use the default loop. This value SHOULD NOT be given unless you're sure you want to explicitly use a given event loop instance.

The returned promise is implemented in such a way that it can be cancelled when it is still pending. Cancelling a pending promise will reject its value with a RuntimeException and clean up any pending timers.

$timer = React\Promise\Timer\sleep(2.0);

$timer->cancel();

resolve()

Deprecated since v1.8.0, see sleep() instead.

The resolve(float $time, ?LoopInterface $loop = null): PromiseInterface<float> function can be used to create a new promise that resolves in $time seconds with the $time as the fulfillment value.

React\Promise\Timer\resolve(1.5)->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.

This function takes an optional LoopInterface|null $loop parameter that can be used to pass the event loop instance to use. You can use a null value here in order to use the default loop. This value SHOULD NOT be given unless you're sure you want to explicitly use a given event loop instance.

The returned promise is implemented in such a way that it can be cancelled when it is still pending. Cancelling a pending promise will reject its value with a RuntimeException and clean up any pending timers.

$timer = React\Promise\Timer\resolve(2.0);

$timer->cancel();

reject()

Deprecated since v1.8.0, see sleep() instead.

The reject(float $time, ?LoopInterface $loop = null): PromiseInterface<never> function can be used to create a new promise which rejects in $time seconds with a TimeoutException.

React\Promise\Timer\reject(2.0)->then(null, function (React\Promise\Timer\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 takes an optional LoopInterface|null $loop parameter that can be used to pass the event loop instance to use. You can use a null value here in order to use the default loop. This value SHOULD NOT be given unless you're sure you want to explicitly use a given event loop instance.

The returned promise is implemented in such a way that it can be cancelled when it is still pending. Cancelling a pending promise will reject its value with a RuntimeException and clean up any pending timers.

$timer = React\Promise\Timer\reject(2.0);

$timer->cancel();

TimeoutException

The TimeoutException extends PHP's built-in RuntimeException.

getTimeout()

The getTimeout(): float 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.10

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 the latest supported PHP version 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:

vendor/bin/phpunit

License

MIT, see LICENSE file.

promise-timer's People

Contributors

aak74 avatar cboden avatar clue avatar jsor avatar nhedger avatar nikograno avatar reedy avatar simonfrings avatar thomas-gelf avatar wyrihaximus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

promise-timer's Issues

Rename await() to timeout()

After talking to @jsor about #2 we agree that the name await() implies a similarity with the await keyword in ES6/HHVM.

The function name "Timeout\await()" combined makes sense, but not in isolation. As a first step, let's rename this to "Timeout\timeout()".

Obviously, this isn't ideal either, so we should also rename the namespace…

Performance

I read the source of promise-timer , I see that for each timer we have a addTimer , I am working on a project with huge request , about 10M/s

I want to know If we have 10M timer , Is this impact performance?
Is it better that we have a timer that manager all 10M request , I don't need accuracy , for me 3 or 4 sec for timer is not important

Do you have any better idea?

Psalm in combination with v1.8.0

Hi,

Since the recent v1.8.0 release I am having some problems with Psalm.

There is an example listed in the README of this project:

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

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

However when running Psalm with errorLevel 5 or lower I get:

ERROR: UndefinedInterfaceMethod - Method React\Promise\PromiseInterface::otherwise does not exist (see https://psalm.dev/181)
            ->otherwise(function (\React\Promise\Timer\TimeoutException $error) {

I guess this is caused by the return type hint of PromiseInterface that was added to \React\Promise\Timer\timeout.

This timeout() function is returning a new Promise(), would it be an idea to change the docblock to @return Promise or @return ExtendedPromiseInterface?

Promise timer wont stop sub-processes

Hi dear @clue ,
I Think I found a bug!
or I am just confused how this promise-timer works!

When I below Code, My Base Promise Canceled But Sub-processes wont stop and continue their works

<?php

use React\Promise\Promise;
use React\Promise\Timer\TimeoutException;
use function React\Promise\Timer\timeout;

require "vendor/autoload.php";

// Setup EventLoop
$loop = React\EventLoop\Loop::get();

timeout(
    new Promise(function () {
        echo "Task Start" . PHP_EOL;
        return \React\Promise\Timer\sleep(10)
            ->then(function () {
                echo "Sleep Complete and Run" . PHP_EOL;
            });
    }, function () {
        echo "Promise Canceled" . PHP_EOL;
    }), 5)
    ->then(function ($result) {
        // on Success
        echo "Task Success Finalized";
        return true;
    }, function (\Exception $error) {
        // on Timeout/failed
        if ($error instanceof TimeoutException) {
            echo "Task Timeout-ed" . PHP_EOL;
        } else {
            echo "Task Exception" . PHP_EOL;
        }

        return false;
    });


$loop->run();

and Code Response:

Task Start
Task Timeout-ed
Promise Canceled
Sleep Complete and Run

Why it act like that and wont stop sub-processes?
whats wrong in my Ideology؟

Provide function that combines `sleep()` and `timeout()`

For an event-driven API with user interactions, I need to provide endpoints that delay the response for a while to wait for potential interactions and take an action once a timeout is reached.

I've implemented something in a possibly hacky way and believe it would be a potential feature for this lib:

namespace React\Promise\Timer;

use Closure;

use function React\Async\async;
use function React\Async\await;

/**
 * @param Closure():bool $stopCondition,
 * @param Closure():void $onTimeout
 */
function waitUntil(
    Closure $stopCondition,
    float $verificationInterval,
    Closure $onTimeout,
    float $timeout,
): void {
    try {
        $wait = async(static function () use ($stopCondition, $verificationInterval): void {
            while (! $stopCondition()) {
                await(sleep($verificationInterval)); // Timer\sleep() btw
            }
        });

        await(timeout($wait(), $timeout)); // Timer\timeout() btw
    } catch (TimeoutException) {
        $onTimeout();
    }
}

You certainly have ideas on how to turn this into a decent feature or just bin it.
I'm happy either way 😂

Rename to promise-timer

After talking to @jsor about #2 we agree that the packge name should be changed.

The package name should be "promise-timer" instead of "promise-timeout", as only a single function actually deals with timing out promises.

Accordingly, the namespace should be changed from "Promise\Timeout" to "Promise\Timer" (see also #10).

Timeout\await() => Timeout\timeout() => Timer\timeout() (see also #10)
Timeout\resolve() => Timer\resolve()
Timeout\reject() => Timer\reject()

Timer seems to be not working..

Hello,

I've been trying to use your package, however, I do seem to be unable to set it up properly.

To start with, here's my setup:

 ~ php -v
PHP 7.1.8 (cli) (built: Aug 25 2017 10:11:33) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies

Composer.lock contents:

...
"name": "react/promise-timer",
"version": "v1.2.0",
...

Here's a code example:

$loop = \React\EventLoop\Factory::create();

$promise = new Promise(function ($resolve, $reject) {
    $seconds = 10;
    sleep($seconds);
    $resolve("Unexpected Behavior.. Should've thrown an exception..");
}, function ($resolve, $reject) {
    $reject(new \Exception());
});

timeout($promise, 5, $loop)->then(function ($value) {
    die(var_dump($value));
})->otherwise(function (Exception $exception) {
    die(var_dump('Expected behavior..'));
});

The output:

string(53) "Unexpected Behavior.. Should've thrown an exception.."

From my understanding, I should've received the "Expected behavior.." string as a result..

Am I doing anything wrong?

Thank you in advance,
Rad

Next steps for PromiseTimer with ReactPHP v3

We're currently moving forward with working on ReactPHP v3 and releasing the roadmap tickets for all our components (see reactphp/event-loop#271 and others). We still have some components that we haven't finalized plans for, especially with the next major version approaching. It's important to address how we can make sure these components are aligned with the upcoming ReactPHP v3.

This discussion about the PromiseTimer component is somewhat similar to reactphp/promise-stream#40 and https://github.com/orgs/reactphp/discussions/475. We already started to remove additional dependencies of PromiseTimer where possible (see reactphp/socket#305, reactphp/dns#213, clue/reactphp-redis#154 and others) and after taking a look at Packagist, it seems like PromiseTimer is only used as a dev-dependency for tests in other ReactPHP components.

This raises the same questions as in PromiseStream, should we move the ReactPHP PromiseTimer component to Friends of ReactPHP, or should we simply reuse the logic elsewhere and deprecate the component afterwards together with the EOL of ReactPHP v1.

Happy about input on this, so let's discuss possible options and decide on what makes the most sense 🚀

Do not cancel promise once timeout fires

Currently, the promise will be cancelled (edit: rejected) once the timeout fires. This is debatable, but IMHO the name "timeout" implies that we either wait for the promise to settle within the timeout or will never care about its (possible) future result.

Arguably, another use case would be "checking" whether a promise settles within a given time frame. I'd like to put this out here to see if anybody happens to have any thoughts and/or actual use cases.

Also refs clue/reactphp-block#4

Support Promise cancellation

Implementation is relatively straight forward, but non-trivial, considering the number of required test cases.

Support collection operations

What is the recommended way to wait for a timeout of multiple promises?

react/promise provides several promise collection primitives, which all have valid use cases.

One might assume the following is the way to go:

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

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

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

How does this cope with cancellation support? (Refs #9 and #3)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.