RSS
 

Promises, Promises… Timeout!

25 Apr

I was playing with ES6’s new Promises, for the first time, this week. Then, I was looking at the ugliness of using a browser’s setTimeout() function and thought that it would look better as a Promise.

tl;dr summary: A Simple Promise Version of “setTimeout()”

If we do it right, you simply specify the timeout period and implement a handler for then() to invoke:

timeout(5000) // Delay for 5000 ms
   .then(function () {
      // Do, here, whatever should happen when the time has elapsed…
   });

Or, since this is ES6, we might as well use arrow-function shorthand:

timeout(5000).then( () => {
   // do your thing, here…
})

Implementation

The setTimeout() already functions a bit like a Promise, without matching the promise-pattern and syntax, so converting it to a Promise is pretty easy:

function timeout(delay) {
   return new Promise( 
      (resolve, reject) => setTimeout( resolve, delay )
   )
} // timeout()

This all, in contrast to the “old” way of using setTimeout():

setTimeout(function () {
   // This is where I do my thing, when the timer pops…
}, 5000);

It just looks ugly; the invocation of the timer and its delay-time property is mingled with what happens when the timer triggers. With the promise pattern, the semantics of the invocation and the handling are discretely organized between the timeout() and its then() handler.

Missing Promises

There are a few things missing.

  1. Cannot cancel a timer.
  2. Even if the timer were cancelable, there ought to be a way to handle it, just as a Promise allows.

We want a cancel() (or perhaps reject()) method. And, if the timer is canceled the promise would be rejected; maybe like so:

let timer = timeout(5000)
.then(id => console.log(id+' completed'))
.catch(err => console.log(err, ' caught!!!'))
// timer.cancel() // Can be canceled

So, how about this… the timeout() function remains dead-simple:

function timeout(delay) 
{
    let promise
    let timeout_reject;     // Save reject() for use during cancel()
    let timeout_timerid;    // Save setTimeout() id for use during cancel()
    function resolver(resolve, reject) // Resolver implements timeout
    {
        timeout_reject = reject
        timeout_timerid = setTimeout(() => resolve(timeout_timerid), delay)
        console.log('resolver:', delay, timeout_timerid )
    }
    promise = new Promise(resolver) 
    promise.cancel = () => {
        clearTimeout(timeout_timerid); 
        timeout_reject(`Timer ${timeout_timerid} canceled`) 
    }
    return promise          // Return promise for "use" by caller
}

A cancel() method is attached to the Promise instance, returned. If it is ever invoked, the timer is canceled and promise’s reject() is invoked.

It feels like this is desiring a class to encapsulate the settings attached to the Promise. It bloats the code, and there isn’t much of a win, so this is just for consideration.

function timeout(delay) {
    class Internal {
        attach(promise) {
            promise.cancel= this.cancel.bind(this)
            promise.timer = this
        }
        resolver(resolve, reject) {
            this.reject = reject
            this.timerid = setTimeout(resolve, delay)
            console.log('resolver:', delay,this.timerid) 
        }
        cancel() {
            clearTimeout(this.timerid); 
            this.reject(`timer ${this.timerid} canceled`)
        }
    }

    let tracker=new Internal()
    let promise = new Promise(tracker.resolver.bind(tracker))
    tracker.attach(promise)
    return promise
}

If I were more expert, I might know how to integrate the “internal” class so it appears to derive from Promise. Part of the challenge is that the a resolver function must be passed to the Promise constructor; this prevents useful things from being done before the Promise constructor but before the resolver is invoked.

The slowness of talking across the Internet to grab the various blobs of information and services has pushed web-based applications away from sequential, serial processing towards programs that handle tasks simultaneously…or, at least, do not block the user from seeing and interacting with the application. Part of the evolution is to make it easier for programmers to write such code. I was gonna write an intro to Promises, here, but it’s already been covered by a lot of others, very well. Take a look at the YouTube videos, below.

I’m open to suggestions about how to make this a universal replacement for setTimeout(). Let me know what you think.

Resources

 
 

Tags: , , , ,