DEV Community

Spacie
Spacie

Posted on • Updated on

Thenables: Await objects and cancel or defer your promises!

You're likely already using await with promises to receive values without creating a callback:

async function example(){
    const data = await loadSomething()
    // Use data for something...
}
Enter fullscreen mode Exit fullscreen mode

But did you know you can use await with objects other than promises?

The await syntax can be used to wait for the completion of any thenable (an object containing a then function that takes a callback)! For example:

function wait(time){
    return {then: done => setTimeout(done, time)}
}

async function example(){
    console.log('Hello...') // Logs immediately 
    await wait(1000)
    console.log('World!') // Logs after one second
}
Enter fullscreen mode Exit fullscreen mode

This also means that instances of any class with a then method can be used with await:

class Wait {
    constructor(time){
        this.time = time
    }

    then(callback){
        setTimeout(callback, this.time)
    }
}

async function example(){
    const delay = new Wait(1000)

    console.log("Hello...")
    await delay
    console.log("World!")
}
Enter fullscreen mode Exit fullscreen mode

Using thenables it's super easy to implement deferred and cancelable promises. This allows you to skip to the end of a promise or stop it from ever resolving at all!


class Deferred {
    constructor(){
        this.canceled = false
        this.promise = new Promise((resolve, reject) => {
            this.resolver = (value) => {
                if(!this.canceled) resolve(value)
            }
            this.rejecter = (value) => {
                if(!this.canceled) reject(value)
            }
        })
    }

    resolve(value){
        this.resolver(value)
    }

    reject(value){
        this.rejecter(value)
    }

    then(onFulfilled, onRejected){
        this.promise.then(onFulfilled, onRejected)
    }

    cancel(){
        this.canceled = true
    }

}
Enter fullscreen mode Exit fullscreen mode

Do you use thenables over promises anywhere in your codebase? Share your use cases in the comments!

Also, thanks for reading my first post!

Latest comments (2)

Collapse
 
einicher profile image
Markus René Einicher • Edited

When I use then to return “this” with its callback it starts an infinite loop of calling then. I don't get why:

class ApiRequest {

    constructor() {

    }

    then(ready) {
        console.log('then start');
        ready(this)
        console.log('then end');
    }

}

let apiRequest = new ApiRequest;
await apiRequest;

console:

then start
then end
then start
then end
then start
then end
then start
then end
then start
then end
then start
then end
then start
then end
then start
then end
then start
then end
...

With .then style it works perfectly:

let apiRequest = new ApiRequest;
apiRequest.then(apiResponse => {
    console.log('apiResponse', apiResponse)
});
Collapse
 
pavermakov profile image
Pavel Ermakov

Nice one! By the way, do you have an example of how to use it?