Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method chaining with async/await in TypeScript

I have a situation where I need to call an async method on the result of an async method.

class Parent {
  constructor(private child: Child) { }

  private getChild(): Promise<Child> {
    return Promise.resolve(this.child);
  }

  async getResult(): Promise<Child> {
     return await this.getChild()
  }
}

class Child {
  getText(): Promise<string> {
    return Promise.resolve('text');
  }
}

let child = new Child();
let container = new Parent(child);

let printText = async () => {
  await (await container.getResult()).getText();
}

printText();

Is there a good way to avoid the need to double await? I think I'd like to just do await container.getChild().getText();. What's the right way to make an API in TypeScript that will allow me to chain methods that return promises and then wait for the result with a single await?

EDIT: To clarify, this is more of an API design question. Is there a better pattern for doing what I'm trying to do (call an async method on an object returned by an async method)? Even if it means doing something totally different?

like image 211
heathkit Avatar asked May 05 '17 01:05

heathkit


People also ask

Can we use async await in TypeScript?

Async - Await has been supported by TypeScript since version 1.7. Asynchronous functions are prefixed with the async keyword; await suspends the execution until an asynchronous function return promise is fulfilled and unwraps the value from the Promise returned.

Can you mix async await and promises?

I generally recommend sticking to async/await syntax. There's no need to mix the two syntaxes because if a function returns a promise, you can also use async/await. Async/await is built on top of promises after all. An exception to this rule is when you cache promises or use Promise.

What is difference between promise chains and async await?

In promises, the promises chain alone is asynchronous, whereas in async/await the entire wrapper function is asynchronous.

Why async await over simple promise chains?

Promise chains can become difficult to understand sometimes. Using Async/Await makes it easier to read and understand the flow of the program as compared to promise chains.


1 Answers

Is there a way to design my API better so that users won't need the double await? Even if it means changing the structure of my example drastically.

You must not make getResult asynchronous, it needs to return a Result instance right away so that further methods can be called on it. Your example is a bit weird since getChild doesn't really need to be asynchronous at all. But lets assume it is, and does some important.

You then could write

class Parent {
  private async getChild(): Promise<Child> {
    … // whatever
  }

  getResult(): Result {
     return new Result(this.getChild())
  }
}
class Result {
  constructor(p: Child) {
    this.promise = p;
  }
  async getText(): Promise<string> {
    return (await this.promise).getText();
  }
}

Now you can call parent.getResult().getText() directly. Basically, Result acts as a proxy wrapper around the Child class that does both the awaits. Maybe in your actual architecture you can even avoid the this.promise and do the child and text accesses in one step.

However, usually this is not worth it. Just let your caller await each step.

like image 175
Bergi Avatar answered Oct 12 '22 07:10

Bergi