That's a great question actually.. In any evented app, blocking your "reactor" is a big performance problem, since everyone will be waiting for you to complete that operation before anything else can happen. In general, you want to turn any CPU intensive work into an IO-bound operation, where the reactor is "waiting" for the IO notification that the computation is complete.
Now.. How you actually achieve that is a whole different story. You could, in theory, throw a job into some external work queue and poll that, or if your runtime permits, spawn some threadpool and periodically check that, or.. spawn a process and wait on that. In other words, it all depends on the actual operation.
In the case of PDF generation, if you rely on some external tool, you could use a mechanism like EM.system('shell cmd') to spawn a process and wait for it to return you the data.