All asynchronous magic would be impossible without Even loop. It is the core of any asynchronous application. We register events and handlers for them. When an event is fired the event loop triggers an appropriate handler. This allows a caller to instantiate an operation and continue without waiting for this operation to be completed. Later the caller will be notified about completion.
So, let’s start with some Hello world examples.
To run some code inside the loop we can use
Loop::run() method. It accepts a callback. Then this callback deferred.
This snippet is not really very interesting, the code here looks synchronous even if it uses an event loop. When running this script we receive an expected output:
But it perfectly illustrates the integration of the event loop into a synchronous PHP script. Everything before the loop executes synchronously as it is. Then event loop receives flow control and executes everything inside it. When all schedules tasks are done (or you explicitly stop the loop with
Loop::stop() call), the flow control leaves the loop and continues synchronously executing the script.
Now, let’s try something more complicated:
With this script we now can see asynchronous execution and that the flow has changed:
That happens because when we schedule some code with
Loop::defer() this code is deferred to execute in the next iteration of the event loop. In our example, the first iteration of the loop has one
echo 'inside loop' . PHP_EOL call. The scheduled code will be executed when all code in the first iteration is done.
Loop::run() implicitly defers passed callback. This can be demonstrated by scheduling a callback before running the loop:
The output shows that the first deferred callback is executed before the callback, which is passed to
To delay some code event loop has
setInterval(). It has the same set of arguments as
setTimeout() does. It also schedules a specified callback, but instead of executing it once, this callback is being repeatedly executed after a specified period of time.
To repeatedly execute some code event loop has
If you run this code you will see that it endlessly spams your terminal with
Hello world string. Why?
Do you remember how event loop works? It takes the flow and executes all scheduled tasks.
Loop::repeat() call will endlessly schedule a task until you explicitly cancel it. Behind the scenes,
Loop::repeat() creates a timer watcher and returns its id. This id is also passed to a specified callback as a first argument. So, to cancel this timer you should explicitly call
Loop::cancel() and provide a watcher’s id:
The code above output
Hello world five times and then cancels the timer.
The same result can be achieved by stopping the loop:
Loop::stop()doesn’t immediately break the callback. That is why we can see exactly 5
Every time when we schedule some code with
delay() behind the scenes event loop creates a timer watcher. This timer watcher contains information about callback, data associated with it, timer id, and the way this callback will be executed (once, once after a given time or repeatedly). All watchers can be canceled via
Loop::cancel(), but in situations when you need to repeatedly cancel and register them it is preferred to pause and then resume the watcher.
Loop::disable($watcherId) method pauses a watcher with a specified id. To resume a paused watcher use
In the snippet above we schedule code
echo 'Repeat' . PHP_EOL to repeatedly execute every half a second. Then we set up two delays: the first one pauses our repeated code, then the second one resumes it. If you run this code you will see the following:
echo statement executes twice and then the watcher is paused. Then we resume a watcher and in continues spamming with
It’s important to always cancel persistent watchers once you’re finished with them or you’ll create memory leaks in your application.
What Is Hidden Behind Event Loop?
Do I need to install additional extensions to make all this magic work? Not necessary. You can download Amp via composer and start writing asynchronous code, no additional extensions are required. While there are several extensions with event-loop implementations: pecl/ev, pecl/event, php-uv, none of them is required. And Amp has drivers for all of them. Basically, the main difference between different loop implementations lays in performance characteristics. Behind the scenes,
Amp\Loop is clever enough to detect your environment and to choose the best available driver for it. Also, it is OK, if you don’t have any installed extensions, in this case, Amp will use
NativeDriver. While each implementation of the event loop is different, your code should not depend on the particular loop implementation.
This was an introduction to event loop basics. We have started writing asynchronous code by scheduling some code. Event loop is a core of every asynchronous application. It registers events and when these events are fired it triggers appropriate handlers (callbacks). You may consider event loop as a task scheduler. When for example, we
delay() some code, the event loop registers a timer watcher. When a timer is out (the event has happened) event loop dispatches an associated with this timer callback (our delayed code). Once there are no more registered events event loop is done and stops, the flow control returns back to a synchronous PHP script.
You can find examples from this article on GitHub.