JavaScript Promises: That Lightbulb Moment

Blog Post created by jbarry-esristaff Employee on Apr 14, 2019

If you’re not big on preambles, out of time, or otherwise want to cut straight to the mustard, skip down to STEP 4.


When it comes to using Promises, I found the concept to be fairly simple to understand but kept hitting mental hurdles when I tried to actually implement it. I mean, the concept is really simple; it is.


There are so many blog articles out there, official doc, video clips, tutorials, all kinds of resources out on the web that are easy to find. Any time I tried to follow them, I would hit a spot where the author is assuming something that I just didn't have. Not to mention that many of them try to explain it in a way that includes, right from square one, all the capabilities and possible pitfalls.


All of the stuff out there is really well-meaning, especially the ones that try to connect the concept to some type of real-world analogy. But again, for me, I totally understood the concept, rather it was the implementation that I was stuck on. 


I mean, I get that. They want to make sure their explanation isn't so simple that it ignores exceptions or things that could go wrong. They don't want to leave you asking, "yeah but what about…?". And that's fine. But my take though, is that the first time you learn a concept, you should see it in its simplest, perfect state. That way later, when you see exceptions, you'll have a better chance in handling them, because you first got your head wrapped around how it's supposed to work. Let's crawl first, get that down, and then try to walk, then run. 


As with some other programming concepts, sometimes it takes a bit to get to that "lightbulb moment". Like back in school, learning how pointers work in C. That moment of clarity when you just get it, and you ask yourself, "Oh I get it now; how was this so tough?", or better yet when you say to yourself ,"If I could go back in time, I know EXACTLY how I would explain it to myself, so that I'd get it."


So... before I forget what it's like to not know something, I'm going to show you right where the light switch was.


No analogies, no concepts that are beyond basic coders, just the simplest example I could come up with to show HOW TO make a function call using Promises. 






Feel free to skip down to STEP 2 if you already understand why Promises exists.


Most of the doc out there says that you can use a Promise object when you want to call a function, but you're not sure how long it's going to take for that function to return. You may have some code later on that depends on whatever is returned by that function, and you need to make sure that value is there done and ready before you try to use it.


Reading through the doc and other samples, you'll see the term "asynchronous" a lot, meaning, the code in your browser is not running in a single thread where each line of code runs, and then doesn't go to the next line until that previous line is done. No, no it doesn't do that. JavaScript code doesn't wait. If you write 10 lines, then 10 lines will run, schwoop, regardless of how long it takes each line to do what it does. This can cause problems when lines below depend on lines above being done. 


When you know some lines of code will take some time to run, Promise objects will help you line things up.


In other words: A Promise object helps you make asynchronous things, synchronous.



Check out this 6-line pseudocode:

  Line 1: does this

  Line 2: does that

  Line 3: calls a function that returns X, but the function being called takes a few seconds to return

  Line 4: calls a function that takes X as an input, and returns Y

  Line 5: does this

  Line 6: does that


In JavaScript, Line 4 will often fail, because as soon as Line 3 is called, Line 4 will run. And since Line 3 hasn't returned with the X that Line 4 needs, the function in Line 4 won't work. Line 4 has no idea how to wait for whatever you did at Line 3 to be done. It just keeps on going.


Here's how to fix that. What you do is create a Promise object, wrap it around Line 3, and then you wrap a call to that Promise object around Line 4. That way, the function in Line 4 won't even try to run until the function in Line 3 returns the X it needs. 






First, I want to show you how this works without Promises. It's the most basic example I could come up with and should be familiar to anyone who's had more than a few days of experience with JavaScript.


In short, below is a web page that has a button.

When the user clicks that button, a function adds 1+2, and then displays the number 3 in an alert() box:




Here's what the code looks like:


without Promises 


Let's walk through this, line by line:


Lines 5 and 6: Let's set up some values we're going to add together, a = 1 and b = 2.

Line 35: Here's an HTML button, that when clicked, calls the addThem() function.

Line 17: When the user clicks the button, this addThem() function runs.

Line 20: The values a and b are grabbed by the addTwoNumbers() function.

Line 9: The value a is referenced inside the addTwoNumbers() function as the parameter numFirst.

Line 9: The value b is referenced inside the addTwoNumbers() function as the parameter numSecond.

Line 11: The two input values are added together, and a 3 is put into the variable twoNumbersAdded.

Line 13: The value 3 is returned back to Line 20.

Line 20: The result of the addTwoNumbers() function (3) is passed into the variable numsAdded.

Line 25: The contents of the numsAdded variable (in our case: 3) is displayed in an alert() box.



In short, Line 25 needs the numsAdded value that is returned by Line 20. In the example code above, the only reason Line 25 doesn't fail, is NOT because it waited for Line 20 to finish. Rather, the only reason Line 25 works is due to the fact that what's going on inside the addTwoNumbers() function happens so fast, that by the time Line 25 gets around to running, the function called in Line 20 happens to already be done, and the numsAdded value that Line 25 needs is ready. 


Some functions in JavaScript take a bit of time to do. We're coding for the web, after all. Your function might be making a REST call to a remote web server to retrieve, process, or analyze some data before it can return. In our case above, we're just taking two numbers and adding them. It's all happening client-side and happens faster than you can imagine.


But that there can be the problem. Meaning, what if the addTwoNumbers() function had some processing in there that made it take a few seconds to finish? How do we make Line 25 wait for Line 20 to be done?


Promises, that's how. Ok, so here we go...





What we do is this:


with promises


Let's walk through this:


Line 19: We create myPromiseObject and wrap it around Line 20. I've left Line 20 exactly where it is, but added Lines 19, 21, and 22. Rather than calling the addTwoNumbers() function when we get to Line 20, we simply make the addTwoNumbers() function PART OF the configuration of the new Promise object. We don't call the addTwoNumbers() function just yet.


Line 24: Rather than calling the addTwoNumbers() function directly, we instead call myPromiseObject with the .then() method. The .then() method is the magic part—it will wait for the function inside myPromiseObject to finish.


Lines 19 and 22: Inside these two curly braces { } go the lines of code that will run when myPromiseObject is done ready (i.e., when the code inside myPromiseObject is done running).


Line 21: When myPromiseObject resolves (i.e., gets a value back from) the addTwoNumbers() function that it calls, the value in the numsAdded variable goes back to the place myPromiseObject was called from. This will become the "result" that you see on Line 24.


Line 24: When the addTwoNumbers() function inside myPromiseObject finishes, the "result" parameter contains whatever was passed to the resolve() on Line 21.  In other words, the numsAdded variable from Line 21 becomes the result parameter on Line 24.


Line 25: Now that the .then() magic is triggered, the code inside the .then() method’s curly braces runs. In our case above, the value in the "result" parameter is displayed in an alert() box.


Very important to note that in the STEP 2 example without Promises, the alert() box displays the numsAdded variable, but with Promises here in STEP 3, the alert() box instead displays the result parameter. In both cases, the result is the integer 3, but just a different way to get it. 




Here is some runnable sample code:  https://codepen.io/JimBarry/pen/jJgNBd


Here is a Github repo that contains all these details and more: https://github.com/JimBarry/promises-how-to


With some commenting, and uncommenting, you can see how things run both ways, that is, making the same simple function call without a Promise, and then again with a Promise.


In short:


…a Promise object helps you make asynchronous things, synchronous


Anyone pretty experienced with Promises will be able to come up with exceptions and "what about"s that could make this a bit more complicated. For example, not all Promises resolve. Sometimes the function the Promise calls fails, in which case, the "reject" parameter in Line 19 comes into play. Very often, where Line 20 starts will actually be an if conditional block that does this if the Promise resolves and does that if the Promise rejects. Another is if you have more than two lines of code that depend on one another, you may need to chain several promises together so that they execute and resolve in the right order. I could go on...


But... I'll leave that and everything else to your next steps in understanding how Promises work, using other doc and other sources. Like this one. Or perhaps this one. So yeah--this was just the first step. I just wanted to show you a perfect world scenario first, and once you get that, then you'll be ready to understand what's next.