Limiting How Long await Waits
Often you need a guarantee that an operation won't take longer than some given time. For example, if UserDirectory doesn't respond for too long, it can make the API feel broken. There are two possible solutions here:
- Set a timeout at the level of the
file_get_contentsoperation and change the function's code. - Add a limit directly to
await.
use function Async\timeout;
use Async\OperationCanceledException;
try {
$isValid = await($validation, timeout(2000));
} catch (OperationCanceledException $e) {
$validation->cancel();
} catch (RemoteApiException $e) {
}The benefit of limiting await is that there's no need to change the code of validateToken. At the same time, notice the catch (OperationCanceledException $e), not catch (TimeoutException $e) as you might expect.
OperationCanceledException
timeout() throws nothing by itself. It returns a cancellation token — an Async\Timeout object. The code below will never throw, no matter how long the script runs:
use function Async\timeout;
$token = timeout(2000); // just an object; nothing happensThe token only takes effect once it is handed to an operation as a cancellation argument:
use function Async\timeout;
use Async\OperationCanceledException;
try {
$isValid = await($validation, timeout(2000));
} catch (OperationCanceledException $e) {
$validation->cancel();
echo $e->getPrevious()->getMessage(); // message from TimeoutException
}Note that when the token trips you get an OperationCanceledException, not a TimeoutException. The timeout itself sits inside, in getPrevious(). This is intentional, to simplify the try-catch handling logic for await and to clearly distinguish a cancelled wait from an exception raised inside the coroutine. Coroutines normally should not throw OperationCanceledException themselves.
The thing used to limit an await wait doesn't have to be timeout(); it can also be any other coroutine, or a Future, a logical contract representing the completion of some arbitrary operation.