Combination of Async and Generator Function

Hi everyone. In my last blog post, I wrote about the basics of generator function using synchronous operation. In this blog, we’ll cover an advanced ways of using Generator function with asynchronous operation.

If you are not familiar with Async and Generator operation, please give these two blog posts a read. They are short reads and this blog post is certainly not going anywhere.

  1. Achieving Concurrency with Async and Await
  2. Up and running with the Generator Function

. . . . . . .

Welcome back! I hope gave the two articles a brief read and understand the  basics of what the Generator and Async operations are all about.

 

Imagine Roger Federer and Novac Djokovic commencing on an epic tennis match. After few seconds, one of the them will fail to return the ball and the match will end. In this post, we’ll break down a codebase related to the tennis and relate how generator function and asynchronous operations come into play.

 

Warning:

Before we go any further, I recommend creating your own Javascript environment if you wish to run this code base. At the time of writing this blog, JSBin’s environment is not fully caught up to run this code. (I will provide a git repository of the code base for you to clone.)

https://github.com/arroyolabs-blog/tennis

 

The two functions below represents each tennis player, hence the name federer() and djokovic(). Other than the difference in names, the two functions are essentially the same.

Each function takes the player’s name and the return success rate of 80% as arguments.  If the player is successful in returning the ball, it returns an object with value true signaling the game to continue. If the player fails, it will return an object with value false which signals the end of the game.

 

//Remember: 'async' key allows 'await' to be used inside the function
//'Await' places hold on the function until the promise is resolved.
 const federer = async () => {
    try {
        //if successful, it will return resolved value
        return await returnBall("Federer", 0.8);
    } catch(failure) {
        //if unsuccessful, it will return failed value
        return failure;
    }
}

const djokovic = async () => {
    try {
        return await returnBall("Djokovic", 0.8);
    } catch(failure) {
        return failure;
    }
}

 


The function below is a generator function that undergoes an infinite loop. Every loop returns results of federer() and djokovic() respectively. Notice the keyword ‘async’ and generator notation: *.

 

 

async function* rally() {
  while (true) {
    yield await djokovic();
    yield await federer();
  }
}

 

 

The function below is a combination of async and generator iteration. This function looks listens for the success and failure of the two tennis players. When async and generator are used together, we use the keyword ‘for await’. When one of the player returns a rejected promise (at 20% rate), the tennis() ends the program.

 

async function tennis() {
	for await (const status of rally()) {
        //As long as the game is in play, it continuously output the status of the game.
		console.log(status.message);
        //If one of the player fails to return the ball, it will signal for the iteration to be broken.
        if(!status.active) {
          break;
        }
    }
    //Signal the end of game
    console.log('Game!');
}

 

The returnBall function is a combination of async and generator loop. The function is invoked by the player functions and returns either a resolved or rejected promise value.

 


const returnBall = (player, returnRate) => new Promise((resolve, reject) => {
    //Determines the player's success of returning the ball
    const random = Math.random();
    //Determine how long it takes for player to return the ball (for that authentic asynchronous feel!)
    const time = Math.random() * (2000 - 500) + 500;
    setTimeout(() => {
        if (returnRate > random) {
            //active is true to signal the game to continue
            return resolve({active: true, message: `${player} hits the ball`});
        } else {
            //active is false to signal the game to end.
            return reject({active: false, message: `${player} fails to return the ball`});
        }
    }, time);
});

 

The returnBall function returns a promise that resolves itself after a randomly generated amount of time between ½ to 2 seconds to emphasize its asynchronous nature. The returnBall also generates the probability rate of the player’s returnBall. If the random value falls below 0.2, the returnBall will send a rejected promise to the player function which then sends those values to the rally() signaling it to stop the ball. If not, the returnBall will send a resolved promise to the player, who then passes on the information to the rally() to continue the infinite loop.

 

Now that the functions in this codebase has been briefly explained, let’s review how they work holistically. The procedure of the function can be broken down into following steps.

  1. Hold off the rally() and await for djokovic() to return a value that holds success/failure
  2. Pass the djokovic()’s value to tennis()
  3. (If Djokovic’s value is success) Hold off the rally() and await for federer() to return a value that holds success/failure
  4. Pass the federer()’s value to tennis()
  5. (If Federer’s value is success) repeat step 1.

 

With everything briefly summarized, let’s run the code. The end result looks the following. In this execution, we see a back and forth between Federer and Djokovic until Federer loses the round. Although subtle, we can see the asynchronous nature of the program as the interval between each rally is made to be inconsistent.

 

 

Of course, this code example is not perfect in terms of how realistic the tennis match is since the player has 20% of failing to “return” the ball even before the tennis match officially begins. And I am sure to have inadvertently offended many of the hardcore tennis fans for my incorrect use of terminologies… (for that sorry!)

But I hope this provides a clear example of how one can use generator function to manage asynchronous operations.

This coding exercise was a doozy.. and although I tried my hardest to simplify as much as possible if you find the explanation inadequate, please provide any questions in the comment section! We keep close eye out for those.

Thank you for reading!

Next Post

Comments

See how we can help

Lets talk!

Stay up to date on the latest technologies

Join our mailing list, we promise not to spam.