Creating the typewriter effect with the use of async generators
Tags: javascript, async, generator, typewriter
Published: 2019-09-02 10:14:22

In this post, we are going to present the process of creating the typewriter effect.

The following codepen illustrates the final result.

{% codepen https://codepen.io/giannisKapa/pen/ExYwaRL default-tab=js,result %}

# Our goal

The functionality we want to achieve is a subset of another library (with more options and methods) called **text-typing**, that I have developed. It is in beta version yet, but available as npm package for anyone who would like to experiment with some of its capabilities.

You can find **text-typing** here:

{% github kapantzak/text-typing %}

For this post, we are going to develop a function that exposes a small api (one method 😋) which then, we can call in order to apply the typewriter effect. This function will take two parameters:

- The text to be typed
- An array of of two numbers that is going to be used as the speed range of the typing process

The call is going to be like that:

    const textHolder = document.getElementById("myTextHolder");
    const speedRange = [100, 600];
    const txt = textTyping(textHolder, speedRange);
    txt.typeText("Hello there!");

# The markup

For start, we need a HTML element to use it as our text holder. Lets use a `<div>` element with id `myTextHolder`, nested in another div element that is going to be used as the wrapper element.

{% gist https://gist.github.com/kapantzak/e279aa9f7ed199281fd6a1c0bb7b2112 file=index_1.html %}

Then, we apply some css to verticaly allign our text (not necessary).

{% gist https://gist.github.com/kapantzak/e279aa9f7ed199281fd6a1c0bb7b2112 file=style_1.css %}

# The JS part

We can now begin writing our js functions, starting with the main function that we are goint to call in order to apply the typewriter effect.

We declare the `textTyping` function that takes two arguments

- `elem`: the HTML element to hold the text
- `speedRange`: the speed range that is going to be used

{% gist https://gist.github.com/kapantzak/e279aa9f7ed199281fd6a1c0bb7b2112 file=index_1.js %}

## Blinking cursor

We are developing a typewriting effect, so, we need to display a **blinking cursor**. For that, we create a `<span>` element and we apply some css animations in order to achieve the blinking effect.

We create the cursor as soon as we call the `textTyping` function:

{% gist https://gist.github.com/kapantzak/e279aa9f7ed199281fd6a1c0bb7b2112 file=index_2.js %}

and we apply the respective css:

{% gist https://gist.github.com/kapantzak/e279aa9f7ed199281fd6a1c0bb7b2112 file=style_2.css %}

## API

Now we are going to expose our simple api, which consists of one method, the `typeText` method! To achieve that, we return an object that has a property named `typeText` and a value of an anonymous function that takes one argument, named `text`.

{% gist https://gist.github.com/kapantzak/e279aa9f7ed199281fd6a1c0bb7b2112 file=index_3.js %}

In addition, we have added another `<span>` element (`section`) that serves the role of an inner text holder, in order to separate the cursor from the text.

## Typing process

Now we have to implement the typing process. Lets try by spliting the text and getting an array of letters. We can iterate this array and insert each letter inside the `section` element one by one.

Furthermore, we need to inject a timeout before each letter injection, based on the speedRange parameter provided to `textTyping` function.

Lets declare a function that takes the `speedRange` array and returns a random number inside the two numbers contained in the array:

{% gist https://gist.github.com/kapantzak/e279aa9f7ed199281fd6a1c0bb7b2112 file=getSpeed.js %}

Here is our first (**not successful**) attempt

{% codepen https://codepen.io/giannisKapa/pen/bGboVGK default-tab=js,result %}

What happened? Why are letters mixed up?

The problem is here

{% gist https://gist.github.com/kapantzak/e279aa9f7ed199281fd6a1c0bb7b2112 file=setTimeout.js %}

The `for` loop instantly iterates the letters array and the `setTimeout` callbacks start to execute at a random time from the loop execution end.

Each `setTimeout` does not wait for the previous timeout callback to be called, as javascript has a non-blocking runtime. On the contrary, each `setTimeout`, instantly pushes a callback to the message queue with a random timeout generated by `getSpeed` function.

For more details about asynchronous execution, you can checkout this article about the event loop.

{% link https://dev.to/kapantzak/js-illustrated-the-event-loop-4mco %}

## Async generators

In order to solve our problem, we need to find a way to, correctly iterate over a  sequence of asynchronous tasks.

**Async generators** come to our resque!

We are not going to get into more detail about async generators or async iterators. For now, we only need to know that async generators provide us with the ability to generate sequences of asynchronous data, that can be iterated and procude the desired outcome.

Now lets apply them to our function.

First, we need to declare a function that returns a **Promise** which gets resolved after a certain amount of time, returning the appropriate letter for us.

{% gist https://gist.github.com/kapantzak/e279aa9f7ed199281fd6a1c0bb7b2112 file=asyncLetter.js %}

We are going to call this function inside our async generator as shown in the following gist:

{% gist https://gist.github.com/kapantzak/e279aa9f7ed199281fd6a1c0bb7b2112 file=asyncGenerator.js %}

Note the `function*` statement, this is the way we declare generators. And because we want an async generator, we prepend the `async` keyword (in fact `async` is not a keyword itself, instead, the combination `async function` is).

All we have to do now is to iterate over the sequence that our async generator produces. We can do that with the use of the `for await ... of` statement like this:

{% gist https://gist.github.com/kapantzak/e279aa9f7ed199281fd6a1c0bb7b2112 file=typeText.js %}

We now have the desired result, as shown in the codepen at the begining of the post.

Thanks for reading 🤗 so far! I hope it was interesting enough! Please leave your comments for any feedback or questions!

# Resources

- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of
- https://javascript.info/async-iterators-generators
- https://jakearchibald.com/2017/async-iterators-and-generators/

{% youtube wrI-Jb0oFyk %}