Antikythera is a tool built specifically to aid in debugging race conditions and other bugs that tend to happen between and during particular stages in the life of your page
Making a new Antikythera is super easy. Most of the time, it's going to look like this:
blah = new Antikythera();
An Antikythera can also be spun up in development mode by passing it a hash like this:
blah = new Antikythera({ development: true });
Development mode will enforce a roadblock on transitioning between stages until you're ready to move on. It doesn't use breakpoints or debuggers, so you'll maintain all of the interaction you'd expect on your page while you're debugging it.
Setting up an Antikythera is all well and good, but it doesn't mean a thing unless you give it some stages to go between! Creating a stage is super simple; all you have to do is give it a name, a function that will handle the transition into that stage, and a function to handle the transition out of that stage:
function transitionIn() { ... }
function transitionOut() { ... }
blah.stage("stuff", transitionIn, transitionOut);
A stage's transition functions can also optionally accept data. Let's set up another stage with a transition that can take data:
function otherIn() { ... }
function otherOut(data) { ... } // Does something with the data
blah.stage("things", otherIn, otherOut);
It's best to make your stages as discrete as possible, so try to make sure to include everything you'd need to back out of a stage in the transitionOut function: unbinding event listeners, DOM cleanup, et cetera. If you find yourself thinking that there are things you wouldn't want to clean up transitioning out of a stage, you're probably in a subflow, and you might want to spin up a new Antikythera to handle discrete changes that are internal to that subflow.
Once you've set up an Antikythera and pumped a few stages in, it's time to start transitioning between them! Let's see an example:
blah.go("stuff");
Assuming you've just spun up this Antikythera and given it a few stages, this will run the transitionIn function we created up above. Let's try going to a different stage:
blah.go("things");
Now, since we were in the "stuff" stage, this will run transitionOut and then otherIn, putting us into the "things" stage! Finally, let's try giving a transition some data:
blah.go("stuff", { out: { hammerTime: true } });
This time, we'll call otherOut with the argument { hammerTime: true }, then transitionIn() without any data.
If you set up an Antikythera in development mode, you already know that it will impose roadblocks upon your transitions. When it does this, it pushes those transition requests into a queue that you may manually execute using its crank method:
blah.crank();
Crank will shift the first request out of the queue and execute it. Subsequent requests can be handled with more calls to the crank method.
Additionally, development mode opens up the ability for you to step back through the canonical stage history of a page with the rewind method. NOTE: If any of your transitions send data, I'd recommend not using this functionality in a production environment, as it will call your transitions again with the same data.
Assuming you've made some transitions already, all you have to do is call rewind:
blah.rewind();
And the Antikythera will back up into the previous stage. When you're in a previous stage, your Antikythera will behave a bit differently. Notably, any stage transitions you make will not be logged, so they won't affect your canonical page history. Additionally, crank will prioritize new transitions (IE ones you initiate while in a previous stage) but, in the absence of new transitions, will also bring you forward one stage in the page's history.