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?
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?
Share Improve this question edited Mar 29, 2019 at 9:12 Dominik asked Mar 29, 2019 at 8:22 DominikDominik 4371 gold badge4 silver badges18 bronze badges 5-
I tried this code in a Codepen, and
return this;
only returnsWalker {t: undefined}
, with onlyt
and without the two methodsgoToMainPage
andgoToProfile
. I'm a bit puzzled by this. Why aren't the methods part of the instanciated class? Can someone explain? – Jeremy Thille Commented Mar 29, 2019 at 8:32 -
@jeremy cause they are part of the prototype?
Walker { ... }
tells you about the inheritance... – Jonas Wilms Commented Mar 29, 2019 at 8:37 -
@JeremyThille it is not code that should work properly, more like sudo code. I updated the example a litte. We would need to pass the
t
object during the instantiation of the class e.g.new Walker(t)
. The t es from another library I'm using. You can just think of it as a object which gives you a bunch of asynchronous functionality. – Dominik Commented Mar 29, 2019 at 9:14 - @JonasWilms so, are methods of a class only part of the prototype and not part of the class itself? That's weird – Jeremy Thille Commented Mar 29, 2019 at 9:18
-
@jeremy Umm...
new Walker
is still an instance, and thats whatthis
is pointing to. And yes, onlystatic
methods are part of the constructor itself, methods are part of the prototype. Remember:class
is just syntactic sugar around constructors. – Jonas Wilms Commented Mar 29, 2019 at 9:41
2 Answers
Reset to default 7await
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
, await
ing 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!
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());