Asynchronous WordPress


This morning, at WordCamp US, I will be giving a lightning talk to a room full of WordPress professionals from around the globe. This talk was given earlier this year at WordCamp Baltimore as a full 45 minute talk, but sadly, I don’t have that kind of time today. So I figured I’d provide those attendees and anyone who might be interested additional context.

In WordPress, there are a “event opportunities”, usually referred to as hooks, that occur throughout the loading of a page and execution of the WordPress code. Which hooks are fired depend on the context. If you’re publishing a post, one series of hooks trigger. If you leave a comment, another series of hooks fire. If you’re in the WordPress admin, a different set of hooks are executed.

All of these hooks exist without any sort of innate overhead. They exist to allow other areas of code, including plugins or themes, to attach events to them. We are used to these things. Plugins have created all kinds of things over the years that might get triggered by a hook.

Some examples:

  • When a post is saved, a third party service analyzes the content and provides a set of suggested tags.
  • When a post is published by an author, an email is dispatched to an editor for review.
  • When a new user is added, information from social sources is pulled in and added to the user’s WordPress profile

All of these sorts of events add liability to WordPress in the form of processing time or load time. In the case where many events are queued up to be executed or, particularly, third-party APIs are slow to respond or possibly even down, the execution time in WordPress will be increasingly long while the web server waits for responses. This is a bad user experience as the user may just be sitting there watching and waiting for something to happen. In extreme cases, this waiting may timeout resulting in errors.

Some time ago, 10up was engaged by TechCrunch to solve a long and lingering problem with the site load time. One of the bottlenecks discovered to performance was a series of events that were occurring, particularly around the Crunchbase API at the time, that was causing significant delays as every occurrence of the save_post hook also carried a call to the API to get information about investors, startups and key people mentioned in the article. and render the information in the article.

We created a library that TechCrunch open-sourced and that is the topic of this talk. This library essentially offloads events that would be on a hook like save_post and fires it asynchronously 1.

This WP_Async_Task class can be included as many times as wanted as it will ever only ever be included once thanks to a class_exists() check.

Once this core class is included, it’s time to get to work. For every hook (not event) that you wish to fire asynchronously, this class must be extended with a protected $action variable which contains a string identifying the hook name and two required protected methods – prepare_data() and run_action().

The prepare_data() method exists to take a numeric index of data passed into it. The purpose of this method is to return data identical to what would traditionally be passed into the hook you’re making asynchronous. The save_post hook takes three optional arguments 2 — $post_id (integer), $post (object of type WP_Post), $update (boolean).

The run_action() method accepts the returned array from the prepare_data() method as a $_POST superglobal, so normal sanitation should also happen in the context of this method. This method is where you would define your newly named hook “wp_async_save_post”. Code examples can be seen on the Github page.

Asynchronous WordPress from Aaron Brazell

Notes:

  1. More like faux-asyncronously since they are really just moved to the end of execution on the shutdown hook
  2. Despite some arguments, with some hooks, being optional to use, they should not be optionally returned in these classes, as any event from any plugin may or may not use all three arguments — return all data that is available to be used by the hook!