blog

  • Home
  • blog
  • Writing Async Libraries – Let’s Convert HTML to PDF

Writing Async Libraries – Let’s Convert HTML to PDF

I can barely remember a conference where the topic of asynchronous PHP wasn’t discussed. I am pleased that it’s so frequently spoken about these days. There’s a secret these speakers aren’t telling, though…

Making asynchronous servers, resolving domain names, interacting with file systems: these are the easy things. Making your own asynchronous libraries is hard. And it’s where you spend most of your time!

Vector image of parallel racing arrows, indicating multi-process execution

The reason those easy things are easy is because they were the proof of concept – to make async PHP competitive with NodeJS. You can see this in how similar their early interfaces were:

var http = require("http");
var server = http.createServer();

server.on("request", function(request, response) {
    response.writeHead(200, {
        "Content-Type": "text/plain"
    });

    response.end("Hello World");
});

server.listen(3000, "127.0.0.1");

This code was tested with Node 7.3.0

require "vendor/autoload.php";

$loop = ReactEventLoopFactory::create();
$socket = new ReactSocketServer($loop);
$server = new ReactHttpServer($socket);

$server->on("request", function($request, $response) {
    $response->writeHead(200, [
        "Content-Type" => "text/plain"
    ]);

    $response->end("Hello world");
});

$socket->listen(3000, "127.0.0.1");
$loop->run();

This code was tested with PHP 7.1 and react/http:0.4.2

Today, we’re going to look at a few ways to make your application code work well in an asynchronous architecture. Fret not – your code can still work in a synchronous architecture, so you don’t have to give anything up to learn this new skill. Apart from a bit of time…

You can find the code for this tutorial on Github. I’ve tested it with PHP 7.1 and the most recent versions of ReactPHP and Amp.

Promising Theory

There are a few abstractions common to asynchronous code. We’ve already seen one of them: callbacks. Callbacks, by their very name, describe how they treat slow or blocking operations. Synchronous code is fraught with waiting. Ask for something, wait for that thing to happen.

So, instead, asynchronous frameworks and libraries can employ callbacks. Ask for something, and when it happens: the framework or library will call your code back.

In the case of HTTP servers, we don’t preemptively handle all requests. We don’t wait around for requests to happen, either. We simply describe the code that should be called, should a request happen. The event loop takes care of the rest.

A second common abstraction is promises. Where callbacks are hooks waiting for future events, promises are references to future values. They look something like this:

readFile()
    ->then(function(string $content) {
        print "content: " . $content;
    })
    ->catch(function(Exception $e) {
        print "error: " . $e->getMessage();
    });

It’s a bit more code than callbacks alone, but it’s an interesting approach. We wait for something to happen, and then do another thing. If something goes wrong, we catch the error and respond sensibly. This may look simple, but it’s not spoken about nearly enough.

We’re still using callbacks, but we’ve wrapped them in an abstraction which helps us in other ways. One such benefit is that they allow multiple resolution callbacks…

$promise = readFile();
$promise->then(...)->catch(...);

// ...let's add logging to existing code

$promise->then(function(string $content) use ($logger) {
    $logger->info("file was read");
});

There’s something else I’d like us to focus on. It’s that promises provide a common language – a common abstraction – for thinking about how synchronous code can become asynchronous code.

Let’s take some application code and make it asynchronous, using promises…

Continue reading %Writing Async Libraries – Let’s Convert HTML to PDF%

LEAVE A REPLY