JavaScript Promises Explained By Gambling At A Casino

If you have ever gambled or watched a movie about gambling, then you can understand promises in JavaScript.

We all love the asynchronous capabilities of JavaScript. In fact, we love them so much that sometimes, we overindulge. And then we get code that looks like this “pyramid of doom”.

Image Credit

This is commonly known as “callback hell” because you probably don’t want to re-read that code and try to understand how everything works, and in what sequence it works. In fact, nobody on your team does either.

A few things are difficult about the above example:

  • Unclear error handling. What happens if something goes wrong?
  • Each function depends on the previous function. You do not need the asynchronous style. You want to make the order clear to others reading the code. When you chain this many functions together, a synchronous style of code will be more readable.
  • You need to continually track the variables for input into a function, and then output. And also track the logic that happens to each output. This becomes exhausting.

You could make this entire process more understandable using promises. If you are like me, you may have heard of promises once or twice, but then ignored them because they seemed confusing. The basic uses of promises are actually pretty easy if you understand callbacks.

Promises encourage straightforward, single-purpose functions that will allow you to write clear code and understand every step without headaches. After thinking about it for a while, I realized that promises are just like a trip to the casino. While a casino “modifies” the amount of money in your bank account (ahem, removes), a chain of promises modifies data in a specific sequence.

So, let’s jump into it. If you do not have experience with callbacks, check out my explanation on the principles of callbacks. If you are looking for a more technical explanation of promises, check out this guide or this guide or this video.

What is a promise?

Let’s say that you are taking a weekend vacation to a casino. You have two weeks of salary in your pocket, and you are going to enjoy every moment as you bet it away, down to the last dime. Or perhaps you will get lucky, and end up winning money?

You get to your hotel room, then head down to the casino. Each type of game accepts cash, so you will need to go to the ATM to withdraw $1000 and get started.

Let’s take a step back and think about this scenario. Although cash can be used for anything outside of the casino, it means one thing inside- the number of games you have left before you run out of money. That cash amount will likely shrink further and further over the course of the weekend. It could also grow, but you have already promised yourself that you will not lose more than $1000 this weekend.

Notice how your remaining money amount gets passed from game to game in the diagram above?

A promise holds the place of a value that does not yet exist, but will certainly exist in the future. This allows you to clearly follow a function and understand its beginning and end. As shown above, promises are a great way of giving clarity to consecutive asynchronous functions and clarifying inputs and outputs.

Promises pass the products of one asynchronous function directly into the next function. That function will start as soon as the previous function has returned a value. Or, if it returns an error, you will run a different function. We can cover that contingency later.

Creating Your First Promise

There are actually two types of promises: producer and consumer.

The producer is the first promise in the chain, while the consumers wait on a result from a previous promise in the chain. In this case, going to the ATM is the producer, since you need money to play games (obviously).

Also, a promise can have one of three states:

  1. Pending- has not completed yet
  2. Fulfilled- Promise has completed and returned a value
  3. Rejected- Promise has completed with an error or failed.

So, if you visit an ATM, and you fail to complete the operation that you intended… well, you may not have the $1000 in your bank account and should exit the casino immediately. If you successfully withdraw $1000, then you have returned a value.

So let’s turn this into code. Here is the promise syntax.

let withdraw = new Promise(function(resolve,reject){

  let amount = visitATM(1000);
  return resolve(amount)
});

And here is an interactive version of that code.

Just like any other asynchronous code, this approach allows your code to wait on the status of the visitATM function. There’s no point in continuing if that isn’t completed!

Chaining Multiple Promises

Let’s assume that you want to play slots, poker and roulette while you are at the casino. Each one requires you to buy-in via cash. Of course, if you bet too much money on poker and run out, then you will not be able to play any of the following games.

Let’s say that you want to play slots first.

 
let withdraw = new Promise(function(resolve,reject){ 

  let amount = visitATM(1000); 

  return resolve(amount) 
}); 

withdraw.then(function(amount){
  let slotResults = playSlots(amount, 100);
  
  if(slotResults <= 0)
    throw err;
    
  return slotResults;
});

Promises use the .then syntax to show what should happen after the previous promise is settled, or completed. In this case, the final result of the withdraw promise is contained within amount.

So, when we set up the next promise using .then(), we also name the argument amount to correspond to that previous result.

One other important note- playSlots is a made-up function. We are imagining that it takes two arguments – the total amount of money you have, and the amount you are willing to gamble.

Let’s add another step to this promise chain- a game of poker. It will work similarly to the slot machine promise. We will gamble as much as we want in this one.

 
withdraw.then(function(amount){
  let slotResults = playSlots(amount, 100);
  
  if(slotResults <= 0)
    throw err;
    
  return slotResults;
})
.then(function(slotResults){
  let pokerResults = playPoker(slotResults);

  if(pokerResults <= 0) 
    throw err; 

  return pokerResults;
})

So, we feed whatever cash remains after playing slot machines into the poker game. Pretty aggressive, if you ask me.

Here’s a code diagram of this part.

Let’s imagine that we have now gambled away all our money. Although we originally intended to play more games, we have no money left. There may be more promises added in this chain, but we will not be able to settle them.

Instead, since we have $0 left after poker, this promise will throw an error. It is still settled, but in a rejected state.

This is where the .catch() method comes in handy. Catch allows us to handle any errors that may occur in our promise chain. We don’t need to write error handlers for each callback.

Let’s imagine that you head straight to the bar when you have gambled all your money. Here’s what that looks like in code.

 
withdraw.then(function(amount){
  let slotResults = playSlots(amount, 100);
  
  if(slotResults <= 0)
    throw err;
    
  return slotResults;
})
.then(function(slotResults){
  let pokerResults = playPoker(slotResults);

  if(pokerResults <= 0) 
    throw err; 

  return pokerResults;
})
.catch(function(e){
  goToBar();
});

This one catch statement will work regardless of which promise is rejected.

Using Objects Within Promises

So far, our promises have only returned a number. But, they can also pass any other type of data along the chain.

Let’s imagine that you played a slot machine, and won some money. The slot machine does not give out straight cash- it gives you a ticket that you can redeem later. That’s called the ticket-in, ticket-out system.

Now, you need to track two values throughout the chain- the amount of cash on hand, and the value of your tickets. An object would work best in this situation.

Let’s modify the second promise in the chain, where you played slots.

 
withdraw.then(function(amount){
  let ticketValue = playSlots(amount, 100);
  
  if(ticketValue <= 0)
    throw err;
    
  return {tickets: ticketValue, cash: amount};
});

You are now returning an object with two properties. Here is what that looks like:

The poker table will only accept cash for chips, so you need to use that property in the next promise.

 
withdraw.then(function(amount){
  let ticketValue = playSlots(amount, 100);
  
  if(ticketValue <= 0)
    throw err;
    
  return {tickets: ticketValue, cash: amount};
})
.then(function(slotResults){
  let pokerResults = playPoker(slotResults.cash);

  if(pokerResults <= 0) 
    throw err; 

  return {tickets: slotResults.tickets, cash: pokerResults};
})
.catch(function(e){
  goToBar();
});

Notice a couple things:

  1. I only used the cash value in the poker game. But, at the end, I still need to add the ticket value into the final object in order to pass it along the chain. Otherwise, I would have lost my winnings.
  2. slotResults contains the object from the previous promise, even though that object did not have a name.

Get The Latest Tutorials

Did you enjoy this explanation? Sign up here to get the latest visualized tutorials of HTML, CSS and JavaScript.

4 thoughts on “JavaScript Promises Explained By Gambling At A Casino

  1. Not your best!! But, could have been better.. Not for someone looking to broaden their knowledge on Promises.

Leave a Reply