loading...

Getting Sleep() with Promises in JS

noamsauerutley profile image noam sauer-utley Originally published at Medium on ・6 min read

Note: this post was originally published on Medium in November, 2019

a small dog sitting under a blanket and looking at an alarm clock

Recently, I started building a very basic single page app to illustrate Fetch API usage in a cute and simple way.

a dachshund putting a tennis ball into a ball-throwing machine

Header: “Welcome to Dog Fetch!”. Yellow button: “Throw the ball!”. smaller text: “You have thrown the ball for: No dogs yet!”

a large brown dog jumping to catch a ball

When the user clicks a button styled like a tennis ball, a new dog image is fetched from the Dog API, and given a name fetched from the Namey API.

a yellow labrador retriever jumping into a pile of leaves of fetch a ball

The dog’s image is loaded onto the page, and with each fetch, a name is added onto a list of fetched dogs.

Header: “Look who it is!”. Below the header, a hound dog looking at the camera.

However, once a few dogs were fetched, the document content stretched down quite far, and required a bit of scrolling to view. I decided to add a handy little link in the page footer which would scroll the viewer right up to the top, and re-fire the fetch cycle.


    function scrollAndThrow(){
        //smooth scroll back to top left corner of window
        window.scroll({
            top: 0, 
            left: 0, 
            behavior: 'smooth' 
           })
        // then click the button to trigger a new round of fetch   
        button.click()
    }

We can just scroll up the window, then trigger a newclick event on the button we’ve assigned to the variable button.

However, the the combo of the page scrolling up and the image fetch simultaneously re-firing looks pretty messy.

the page awkwardly reloading as it autoscrolls up
Yikes.

I wanted a way to slow things down, so the user would only see one thing happening on the page at a time.

I knew that if I could just pause for half a second between the scroll and the API Fetch, everything would look much smoother.

Suddenly, I missed Ruby’s sleep method. It was so nice to be able to schedule events to the exact time I wanted them to run!

a puppy stretching in its sleep

I decided there had to be a way to construct my own dupe of that handy little method.

a dachshund wearing a hard hat and riding on a toy truck

If you’re not familiar with JS Promises, it’s important to first understand that while Javascript is single-threaded (meaning it can only process one statement at a time), it is also asynchronous (meaning it can start processing a new statement before the previous statement has resolved, allowing it to multitask time-consuming processes in the background.).

a dog digging a big hole, kicking sand back onto another dog that is sleeping

Javascript uses Promises to track the resolution of those background processes, allowing us to set certain statements to run only when a time-consuming process has resolved and returned its completed result.

a small dog sitting up on its hind legs and looking out the front door as if waiting for someone

This is great for handling fetch requests and other time consuming processes, but it also gives us a way to tell our app to wait to process certain statements until exactly when we want them to be run.

I realized that I could exploit this functionality by building a Promise around Javascript’s built-in setTimeout() method.

a sad looking puppy sitting in the corner with the caption “He’s in time out”

Let’s make a promise:

setTimeout() takes in two parameters:

1: A function to be executed after the timer expires.

2: The time, in milliseconds (thousandths of a second), the timer should wait before the specified function or code is executed. If this argument is omitted, a value of 0 is used, meaning execute “immediately”, or more accurately, as soon as possible. Note that in either case, the actual delay may be longer than intended

return new Promise(resolve => setTimeout(resolve, ms))

We can create a new Promise, and tell setTimeout to pass the Promise resolve statement as setTimeout’s first parameter. setTimeout will delay for ms milliseconds, then resolve the promise. If we throw this in a function, we now have a handle little delay function ready to go!

 function sleep(ms) {
        // add ms millisecond timeout before promise resolution
        return new Promise(resolve => setTimeout(resolve, ms))
      }

Great! Now we made our replacement for Ruby’s sleep method.

Now let’s put it to use.

I decided to take advantage of one of ES6’s fresher bits of syntax for Promise handling: Async/await.

  async function delayedClick(){
        // await sleep function promise
        await sleep(700)
        // once resolved, click button
        button.click()
    }

delayedClick() knows from the get that it’s awaiting a promise resolution. We’re using our brand new sleep() function as the awaited promise, passing in our desired number of milliseconds (in this case, 700ms , or 7/10ths of a second ). Once that promise resolves, we can enter the action we want delayedClick() to enact. In this case, we’re going to click the button that re-starts the fetch and display cycle.

Now that we have our delay and desired action plugged into a method, we can plug delayedClick() into our existing scrollAndThrow() method as a replacement to button.click() to slow things down and clean up the onscreen process.


    function scrollAndThrow(){
        // smooth scroll back to top left corner of window
        window.scroll({
            top: 0, 
            left: 0, 
            behavior: 'smooth' 
           })
        // use sleep function promise to initiate 700 millisecond delay
        // then click button and initiate new fetch cycle
        delayedClick()
    }

The result: no more jaggedy mid-scroll fetch return and image load!

the app smoothly scrolling up, then reloading
Much nicer!

Sometimes, it’s just good to get some sleep!

a cute puppy falling asleep

Notes:

Posted on Feb 14 by:

noamsauerutley profile

noam sauer-utley

@noamsauerutley

∞ autistic dev with buggy wetware 🧬 they ☿

Discussion

markdown guide