And what to do about it
For my Flatiron Phase 3 final project, my partner and I made a cute little game called Spelling Bee.
The models on the Rails backend were:
A game has many sessions (two to be exact, since there are two players per game), and a player has many games. The Session model is our join.
For every game that is started, two sessions are created, one for each player. These link a unique player with a unique game so that we can track scores. Every time a game is finished we need to update the winner of the game (a string of the player’s name), and both sessions with the associated player’s score. So, we figured, let’s just write three HTTP PATCH requests to the backend and get those three items updated. The code looked something like this:
The result of these three simultaneous requests was a very strange bug. I put a byebug in the ‘update’ controller action to stop the code running there and I found that when calling ‘self.id’ repeatedly I would get alternating session id’s. Ultimately the process would error out with a 500 Internal Server Error. This is a generic HTTP error message that tells us the server encountered some issue that would not allow it to fulfill its request. Sounds about right.
So, you’re dying to know, how did you fix it, Dana?! Through some troubleshooting we confirmed that if we finished a game but only ran one of those three requests, it would correctly update in the database. So the question was, how do we require the code to complete one request before beginning the next? Enter .then.
For Spelling Bee, we used .then after each fetch() to create a cascade of HTTP requests so that each one would not run until the one before it was finished. We called a function containing the next fetch() call inside of each fetch()’s .then statement:
In our scenario it did not matter the order of these HTTP requests, though it could be that a request depends on another one happening before it, in which case you could organize the functions in your .then statements accordingly.
For our purposes this worked well and fixed the bug, though there is another option that has recently become available. The ECMAScript 2017 includes async functions and the ‘await’ keyword. The example from MDN’s documentation is:
An async function is an instance of the AsyncFunction constructor, and the ‘await’ keyword is used inside of the function to tell the engine to hold off on executing the next bit of code until a promise is received from the function directly after the ‘await’ keyword. Test the above code in node or repl.it and you’ll see that the console prints ‘calling’, then after 2 seconds it prints ‘resolved’.
We didn’t have the time to refactor our code to include async/await but it is a slick solution to this common problem, and I think it is more expressive of what we were actually trying to accomplish. Add this to the refactor list!