Dominik
Dominik

Reputation: 437

How to use function chaining with async function?

I'm currently writing an e2e test and I would like to create some classes which abstract certain async tasks for me. In the end I would like to instantiate an object, which let's me chain async functions. Let's say I have a Walker which let's me navigate through the page. I would like to use it in this way:

const walker = new Walker(t)

await walker
  .goToMainPage()
  .goToProfile()

Currently I can only use it like this:

const walker = new Walker(t)

await walker.goToMainPage()
await walker.goToProfile()

This is a rough implementation of how I currently implemented my Walker Class. Where t is and object which allows me to do asynchronous actions within my browser.

class Walker {
  constructor(t) {
    this.t = t;
  }
  async goToMainPage () {
    await t.goTo('url/main')
    return this
  }
  async goToProfile () {
    await t.goTo('url/Profile')
    return this
  }
}

Any ideas on how to create async chainable function calls?

Upvotes: 4

Views: 1412

Answers (2)

Jonas Wilms
Jonas Wilms

Reputation: 138437

await does not only work on Promises, but on every object that provides a .then handler ... therefore your Walker could implement a .then method to allow awaiting:

 class Walker {
   constructor(t) {
     this.t = t;
     // set up a task queue for chaining
     this.task = Promise.resolve();
    }

    // shedules a callback into the task queue
    doTask(cb) {
       // TODO: error handling
       return this.task = this.task.then(cb);
    }

    // allows to use this like a promise, e.g. "await walker";
    then(cb) { cb(this.task); }

    goToMainPage () {
      this.doTask(async () => { // shedule to chain
        await t.goTo('url/main')
      });
      return this; // return walker for chaining
   }

 }

That allows you to do:

 await walker.goToMainPage();
 await walker.goToMainPage().goToMainPage();

If you return something from inside doTask, awaiting it will resolve to that:

 returnStuff() {
   this.doTask(() => "stuff");
   return this;
 }

 //...
 console.log(await walker.returnStuff()); // "stuff"
 console.log(await walker.returnStuff().goToMainPage()); // undefined, result gets lost

Have fun with it!

Upvotes: 7

Jack Bashford
Jack Bashford

Reputation: 44125

You're using async/await - it's essentially a replacement for Promise chaining (and Promises are themselves a solve for callback hell). If you really wanted to use chaining:

walker().then(w => w.goToMainPage()).then(w => w.goToProfile());

Upvotes: 2

Related Questions