Skip to content

Revolt and Amp

Edit this page

Tui runs on the Revolt event loop. This means all I/O performed while the Tui is running must be non-blocking. Blocking calls (file_get_contents(), proc_open(), sleep(), etc.) freeze the entire UI: no rendering, no input handling, no animations.

Why Non-Blocking Matters

The Tui shares a single thread with your application code. The event loop alternates between reading terminal input, running your tick callback and rendering the screen. A blocking call stalls the entire cycle: the screen stops updating, keyboard input queues up and animations freeze until the call returns.

Use Amp Libraries

The Amp ecosystem provides non-blocking replacements for common blocking operations. Since Amp is built on Revolt (the same event loop Tui uses), they integrate without any glue code.

Processes: use amphp/process instead of proc_open() or Symfony Process:

1
2
3
4
use Amp\Process\Process;

$process = Process::start('ls -la');
$stdout = $process->getStdout()->read();

HTTP: use Symfony HttpClient with its Amp integration (symfony/http-client + amphp/http-client):

1
2
3
4
5
use Symfony\Component\HttpClient\AmpHttpClient;

$client = new AmpHttpClient();
$response = $client->request('GET', 'https://example.com');
$body = $response->getContent();

Timers and delays: use Amp\delay() instead of sleep():

1
\Amp\delay(2); // yields to the event loop for 2 seconds

Running Async Work

Use Amp\async() to run work in a fiber. The fiber yields to the event loop whenever it performs I/O, keeping the UI responsive:

1
2
3
4
5
6
7
8
9
10
use Amp\Process\Process;
use function Amp\async;

$future = async(function () {
    $process = Process::start('composer install');
    $output = $process->getStdout()->read();
    $exitCode = $process->join();

    return $output;
});

Check whether the future is complete in your tick callback:

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\Tui\Event\TickEvent;

$tui->onTick(function (TickEvent $event) use ($future, $tui) {
    if ($future->isComplete()) {
        $result = $future->await();
        $tui->stop();
    }

    return !$future->isComplete();
});

The tick callback returns true while work is in progress (the Tui ticks at high frequency) and false when idle. See Tick Loop for the full tick loop API.

Common Pitfalls

Symfony Process is blocking. Process::run() and Process::wait() block the event loop. Use amphp/process instead.

PDO and database queries are blocking. Use an async database library or run queries inside Amp\async() with Amp\Parallel\Worker if you need long-running queries.

sleep() blocks. Use \Amp\delay() for non-blocking delays.

file_get_contents() with URLs blocks. Use AmpHttpClient for HTTP requests.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version