Ky’s (Somewhat Poignant) Guide to Asynchronicity and Cryptocurrencies — Part 2
(Also inconsistently referred to as Ky’s Guide to Starting Over 1.8.2 or Ky’s Guide to Acronyms that Start with ‘X’ in future annotations and subsequent editions)
Lots and Lots of Letters
The collectible marble market was on fire; prices for Symmetrical Clambroths were trouncing old English Divided Core Swirls and trading was happening at breakneck speed. On top of that, Bitcoin and Ethereum prices were fluctuating to boot. If I wanted to keep track of these prices and keep up with the rest of the cryptocurrency/marble traders, I needed to get my info faster than I was able to by clicking through different websites. Luckily, I was working on my CryptoChecker App which, when completed, would let me do just that.
A few tins of stew and a delicious, if not somewhat clumpy, glass of powdered milk had seen me through the bulk of reviewing just how async function calls could be made with JS in my browser environment. But, at that point, I was still a bit foggy on exactly how I could get information from a server using async code. I would need to create some form of request/response cycle, but I was forgetting how that was even possible.
It was time for a bit more work before I could fully flesh out the app; I needed to understand just how I could make those requests with JS, and I also needed to check the perimeter, as it seemed those investment bankers were not easily dissuaded…
A Polite Request
After I had finished politely requesting that the banker remove itself from my front yard and offering a mutually agreeable backup plan for the case in which they might be disinclined to acquiesce, I put my broom back in the closet and turned my attention to making a request with JavaScript.
It turns out, it wasn’t actually that hard. There is a Web API that was built for browsers to do exactly that! The hardest part of the whole endeavor is keeping all the letter straight!
`XMLHttpRequest` (also known as XHR for short) is a Web API provided by all modern browsers; it allows us to create a request object that we can use to retrieve data from a URL without having to do a full page refresh.
You might ask, “What does XML stand for?”
The short answer is, “Ultimately, you don’t need to worry about it.”
The long answer is, “It stands for Extensible Markup Language. Yes, I know extensible doesn’t start with ‘X’, that’s just how they like to make acronyms nowadays…”
This API allows us to make asynchronous calls to outside resources. In my case, I wanted to grab the price of Bitcoin (among other things, but I’ll keep it simple for now); I had the API endpoint, so really, I just needed to set and send the request, then handle the response.
Using this Web API to accomplish this entailed a few different steps:
This looked to be in order, but that bundle of grey matter in my frontal cortex that nags at me to organize and optimize was shouting, “Hey, why not just grab the response body and use it directly after sending!?” This was an interesting premise; I mean, why bother with event listeners at all?
The answer become clear after I tried cutting the event listeners out of the equation altogether:
It became clear that I needed to be mindful that async calls take time! JS will evaluate the code sequentially after all, so if the value I was trying to reference relied on an XHR response that had not returned yet, that value would be undefined when JS evaluated it. The event listeners were the tool that I had to use to listen for the completion of the request, then make use of the data that I had received.
I thought of it like this: Let’s pretend I wanted to get a list of the best manuals on Ornithopter construction to read aloud. I turn to my friend Reginald and ask him to write down his top ten suggestions, but instead of waiting for him to finish writing and hand me the piece of paper back, I just snatch it back and start reading immediately. Would it be surprise to me that there was nothing written on the paper? I should think not!
I had figured out a mechanism I could use to create async request/response cycles to obtain data from a URL, and now I had remembered how to structure my event listeners with this setup so that I could actually obtain the data from the response and use it!
But the more I thought about it, the more I began to realize that this format of creating an XHR request, then relying on the listener to handle the response opened my code up to a realm of headache inducing code blocks that only fans of ancient Egyptian or perhaps Mayan architecture would enjoy…
Building the Pyramids
I got to thinking, “Well, I know how to make an async call to a URL and set an event listener to handle the response, when it arrives. But what happens if I need to make another sync call that depends on the response of the first? How would I set that up? What would that look like?”
In practice, I saw that this often took a common structure: a method that makes an async call takes three arguments: some value needed for the XHR call, a callback to execute if the request is successful, and a second callback to execute if the request is not successful.
That didn’t seem so bad, but once I considered what happened when we had to connect more than one of these methods together, a disturbing pattern emerged.
When you need to make an asynchronous call, then use the data returned from that function to make another async call, you can easily create a long chain of dependent function calls that are very difficult to decipher, also known as a “Pyramid of Doom”!
Consider the following situation:
I want to check the percent growth of bitcoin over the past 24 hours. But there are a few steps I will need to take first. I need to reach out to the API providing pricing data to request the coin id necessary to inquire about a specific coin. Then, with that coin id handy, I need to request the price of that coin. Once I have the pricing data in hand, I can finally request the percent growth. However, I need to account for the possibility that any of those requests might throw an error as well.
For our purpose we are going to abstract out the XHR code and keep the methods here simple, for demonstrative purposes. This might look something like this:
Each method I use is going to make a request that is dependent on its first argument; it will then take a callback to execute if the request is successful, and one other callback in the case of a request failing. Having to pass all of these callbacks in for each method and having each subsequent method dependent on the value returned by the previous request means that we have to structure our code a certain way. This chain of request dependencies and callbacks starts to look an awful lot like a pyramid. A pyramid… of doom.
This kind of code creates serious headaches, and I quickly realized that I should avoid making code like it if I could help it.
There had to be a better way out there to handle async functions such that I could make multiple requests and not have a towering pyramid of dependencies looming in my code.
As it turned out, there was such a way. It exists, I promise! The trick would be leveraging it to make my app as streamlined as I could.
To be continued, or not… We’ll see.
Until next time, or not… We’ll see.
-Ky
Postscript
The story started here. Find out why I started building this app. Also, why you shouldn’t trust investment banking spiders.
The story continues. Promises and pickled herring!