[Revolt](https://revolt.run/) allows such concurrent I/O operations. We keep the cognitive load low by avoiding callbacks.
Our APIs can be used like any other library, except that things _also_ work concurrently, because we use non-blocking I/O under the hood.
Run things concurrently using `Amp\async()` and await the result using `Future::await()` where and when you need it!
There have been various techniques for implementing concurrency in PHP over the years, e.g. callbacks and generators shipped in PHP 5.
These approaches suffered from the ["What color is your function"](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/) problem, which we solved by shipping Fibers with PHP 8.1.
They allow for concurrency with multiple independent call stacks.
Fibers are cooperatively scheduled by the [event-loop](https://revolt.run), which is why they're also called coroutines.
It's important to understand that only one coroutine is running at any given time, all other coroutines are suspended in the meantime.
You can compare coroutines to a computer running multiple programs using a single CPU core.
Each program gets a timeslot to execute.
Coroutines, however, are not preemptive.
They don't get their fixed timeslot.
They have to voluntarily give up control to the event loop.
Any blocking I/O function blocks the entire process while waiting for I/O.
You'll want to avoid them.
If you haven't read the installation guide, have a look at the [Hello World example](https://v3.amphp.org/installation#hello-world) that demonstrates the effect of blocking functions.
The libraries provided by AMPHP avoid blocking for I/O.
Every operation that supports cancellation accepts an instance of `Cancellation` as argument.
Cancellations are objects that allow registering handlers to subscribe to cancellation requests.
These objects are passed down to sub-operations or have to be handled by the operation itself.
`$cancellation->throwIfRequested()` can be used to fail the current operation with a `CancelledException` once cancellation has been requested.
While `throwIfRequested()` works well, some operations might want to subscribe with a callback instead. They can do so
using `Cancellation::subscribe()` to subscribe any cancellation requests that might happen.
The caller creates a `Cancellation` by using one of the implementations below.
> **Note:** Cancellations are advisory only. A DNS resolver might ignore cancellation requests after the query has been sent as the response has to be processed anyway and can still be cached. An HTTP client might continue a nearly finished HTTP request to reuse the connection, but might abort a chunked encoding response as it cannot know whether continuing is actually cheaper than aborting.
#### TimeoutCancellation
A `TimeoutCancellations` automatically cancels itself after the specified number of seconds.
```php
request("...", new Amp\TimeoutCancellation(30));
```
#### SignalCancellation
A `SignalCancellation` automatically cancels itself after a specified signal has been received by the current process.
```php
request("...", new Amp\SignalCancellation(SIGINT));
```
#### DeferredCancellation
A `DeferredCancellation` allows manual cancellation with the call of a method.
This is the preferred way if you need to register some custom callback somewhere instead of shipping your own implementation.
Only the caller has access to the `DeferredCancellation` and can cancel the operation using `DeferredCancellation::cancel()`.
```php
$deferredCancellation = new Amp\DeferredCancellation();
Cancellation is often optional, which is usually implemented by making the parameter nullable.
To avoid guards like `if ($cancellation)`, a `NullCancellation` can be used instead.
```php
$cancellation ??= new NullCancellationToken();
```
#### CompositeCancellation
A `CompositeCancellation` combines multiple independent cancellation objects. If any of these cancellations is cancelled, the `CompositeCancellation` itself will be cancelled.