NestJs Async controller method vs calling await / async inside a method - node.js

I'm bit new to NodeJs & NestJs. I always wondered what's the difference between using async as the method return type inside a controller vs executing async operation inside a regular method ? How does NodeJs handles the request in both these cases if there is huge traffic on this API (eg. 40K req/min). Will it be blocking in the 2nd example and non blocking in the 1st or would it work in a similar way?
For Eg:
#Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
#Post()
async sample() {
return "1234";
}
}
vs
#Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
#Post()
function sample() {
return await methodX();
}
async function methodX(){
return "1234"
}
Please ignore what the content in sample() & methodX() does its only for example.

First, marking a method as async enables you to use the await keyword inside of it.
For example in the second sample you provided, you need to mark the method sample as async to await methodX:
#Post()
async function sample() {
// Now `await` can be used inside `sample`
return await methodX();
}
So to answer your questions
what's the difference between using async as the method return type inside a controller vs executing async operation inside a regular method ?
There is none. In both cases, the method of the controller will need to marked as async. Performing what the route is supposed to perform in the body of the method or extracting it in another async method is just a matter of code organisation.
Will it be blocking in the 2nd example and non blocking in the 1st or would it work in a similar way?
Both examples would work in a similar way.
Second, marking a mehthod as async doesn't actually make it async if there is no real async operation performed in its body like a request to another service or a setTimeout. It means that the following samples
// Sample 1
#Post()
async sample() {
return "1234";
}
// Sample 2
#Post()
function async sample() {
return await methodX();
}
async function methodX(){
return "1234"
}
Are both equivalent to the synchronous method
#Post()
syncSample() {
return "1234";
}
Finally, as stated by #Micael Levi, return await is syntactically correct but should be avoided. Considering this sample:
#Post()
function async sample() {
return await methodX();
}
async function methodX(){
throw new Error('something failed')
}
Because the method sample return await methodX, sample won't appear in the stack trace which makes debugging more difficult.
We would prefer this sample:
#Post()
function async sample() {
const result = await methodX();
return result;
}
async function methodX(){
throw new Error('something failed')
}

Related

Mark an asynchronous service function as asynchronous later in the controller

I have learned that a function with an asynchronous call (e.g. query to a database) is marked as await and the whole function block as async. However, I can apparently define some asynchronous functions without await and call them later with await (in my controller) and for others I am forced immediately to use await in my service class (VSC editor).
I have a user service class with CRUD operations. I can define findOne(), create() and find() without await, even though they perform asynchronous operations. In the controller I use them with async-await and I don't get an error from VSC even if I forget it. However, I have to use my update() and remove() functions in my service class with await because VSC shows me an error and says that I am missing await. Why do the update() and remove() functions have to be immediately marked with await and the others three do not? The functions save(), findOne() and find() have the same Promise return value as my other two functions and access the same repository.
My code (service class):
#Injectable()
export class UsersService {
constructor(#InjectRepository(User) private repo: Repository<User>) {}
create(email: string, password: string) {
const user = this.repo.create({ email, password });
return this.repo.save(user);
}
findOne(id: number) {
return this.repo.findOne(id);
}
find(email: string) {
return this.repo.find({ email });
}
async update(id: number, attrs: Partial<User>) {
const user = await this.findOne(id);
if (!user) {
throw new NotFoundException('user not found');
}
Object.assign(user, attrs);
return this.repo.save(user);
}
async remove(id: number) {
const user = await this.findOne(id);
if (!user) {
throw new NotFoundException('user not found');
}
return this.repo.remove(user);
}
}
Where is the difference and should I then rather always mark all my CRUD operations in the service class immediately as async-await in order to be able to call them later in the controller without async-await?
PS: Sorry if my text is still written too confusing. Why do I have to write await this.findOne() in the function remove(), but I can use this function findOne() with this.repo.findOne(id) without await in the same class, although repo.findOne() is an asynchronous function?
You need to use await because you want the value resolved by the promise returned by this.findOne(id)
And this.repo.find() will return a promise as it's async, thus UsersService#findOne returns a Promise too. So:
return await this.repo.findOne(id) will behave the same as:
return this.repo.findOne(id)
Learn about async/await:
https://nodejs.dev/learn/modern-asynchronous-javascript-with-async-and-await
https://javascript.info/async-await
https://jakearchibald.com/2017/await-vs-return-vs-return-await

await with promise inside try/catch

For example, I have following function:
public static async getUser(userId: number): Promise<UserModel> {
try {
const permissions: PermissionsModel[] = await this.getPermissions(userId);
const userWithRelation: UserModel[] = await this.findUsers(userId, permissions);
return Promise.resolve(userWithRelation);
} catch (e) {
return Promise.reject(e);
}
}
Now, WebStorm warns me that I should put await before Promise.resolve() in try and catch block. I'm pretty new with Node, so should I put await or not and why?
Note: This is just example function, not the real code which I can't publicly show.
If an awaited Promise rejects, its reject value will be what the error in the catch handler is. If it resolves, the value the awaited Promise is assigned to will be the resolve value. So, your code can be simplified to the following:
public static async getUser(userId: number): Promise<UserModel> {
return this.findUsers(userId);
}
awaiting a Promise whose resolve value will be returned immediately, and whose rejection will be returned as a rejected Promise, is superfluous - just return the Promise itself.
I'm not sure about the warning, but you can shorten your code to
public static async getUser(userId: number): Promise<UserModel> {
try {
return this.findUsers(userId);
} catch (e) {
// Do something with the error
return Promise.reject(e);
}
}
If your're not doing anything else in the catch block, then you can actually just omit it and reduce your function to
public static async getUser(userId: number): Promise<UserModel> {
return this.findUsers(userId);
}
Promise and async/await are two different syntax's for asynchronous programming. What WebStorm is warning you about is that since you are using await inside async function, then await should be the thing to be returned from async function and not resolved Promise. You should choose to use either async/await syntax or Promise. You can see the difference on MDN's site:
For async/await: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
For Promises: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Async/await confusion using singeltons

So no matter what I've read, even once I do it right, I can't seem to get the the hang of async and await. For example I have this in my startup.
startup.js
await CommandBus.GetInstance();
await Consumers.GetInstance();
Debugging jumps to the end of the get instance for CommandBus (starting up a channel for rabbitmq) and start Consumers.GetInstance() which fails since channel is null.
CommandBus.js
export default class CommandBus {
private static instance: CommandBus;
private channel: any;
private conn: Connection;
private constructor() {
this.init();
}
private async init() {
//Create connection to rabbitmq
console.log("Starting connection to rabbit.");
this.conn = await connect({
protocol: "amqp",
hostname: settings.RabbitIP,
port: settings.RabbitPort,
username: settings.RabbitUser,
password: settings.RabbitPwd,
vhost: "/"
});
console.log("connecting channel.");
this.channel = await this.conn.createChannel();
}
static async GetInstance(): Promise<CommandBus> {
if (!CommandBus.instance) {
CommandBus.instance = new CommandBus();
}
return CommandBus.instance;
}
public async AddConsumer(queue: Queues) {
await this.channel.assertQueue(queue);
this.channel.consume(queue, msg => {
this.Handle(msg, queue);
});
}
}
Consumers.js
export default class Consumers {
private cb: CommandBus;
private static instance: Consumers;
private constructor() {
this.init();
}
private async init() {
this.cb = await CommandBus.GetInstance();
await cb.AddConsumer(Queues.AuthResponseLogin);
}
static async GetInstance(): Promise<Consumers> {
if (!Consumers.instance) {
Consumers.instance = new Consumers();
}
return Consumers.instance;
}
}
Sorry I realize this is in Typescript, but I imagine that doesn't matter. The issue occurs specifically when calling cb.AddConsumer which can be found CommandBus.js. It tries to assert a queue against a channel that doesn't exist yet. What I don't understand, is looking at it. I feel like I've covered all the await areas, so that it should wait on channel creation. The CommandBus is always fetched as a singleton. I don't if this poses issues, but again it is one of those areas that I cover with awaits as well. Any help is great thanks everyone.
You can't really use asynchronous operations in a constructor. The problem is that the constructor needs to return your instance so it can't also return a promise that will tell the caller when it's done.
So, in your Consumers class, await new Consumers(); is not doing anything useful. new Consumers() returns a new instance of a Consumers object so when you await that it doesn't actually wait for anything. Remember that await does something useful with you await a promise. It doesn't have any special powers to await your constructor being done.
The usual way around this is to create a factory function (which can be a static in your design) that returns a promise that resolves to the new object.
Since you're also trying to make a singleton, you would cache the promise the first time you create it and always return the promise to the caller so the caller would always use .then() to get the finished instance. The first time they call it, they'd get a promise that was still pending, but later they'd get a promise that was already fulfilled. In either case, they just use .then() to get the instance.
I don't know TypeScript well enough to suggest to you the actual code for doing this, but hopefully you get the idea from the description. Turn GetInstance() into a factory function that returns a promise (that you cache) and have that promise resolve to your instance.
Something like this:
static async GetInstance(): Promise<Consumers> {
if (!Consumers.promise) {
let obj = new Consumers();
Consumers.promise = obj.init().then(() => obj);
}
return Consumers.promise;
}
Then, the caller would do:
Consumers.getInstance().then(consumer => {
// code here to use the singleton consumer object
}).catch(err => {
console.log("failed to get consumer object");
});
You will have to do the same thing in any class that has async operations involved in initializing the object (like CommandBus) too and each .init() call needs to call the base class super.init().then(...) so base class can do its thing to get properly initialized too and the promise your .init() is linked to the base class too. Or, if you're creating other objects that themselves have factory functions, then your .init() needs to call those factory functions and link their promises together so the .init() promise that is returned is linked to the other factory function promises too (so the promise your .init() returns will not resolve until all dependent objects are all done).

How to export async function?

following is my code -
abc.js
class abc {
async foo1() {
// do something
return result;
}
async foo2() {
// do something
return result;
}
module.exports = abc
}
another-file.js
const abc = requir(./abc);
abc.foo1();
// this results in error
// TypeError : abc.foo1 is not a function
How should I do this?
Several points. By convention class names starts with capital letter. Your problem has nothing to do with async functions. You have 2 options to solve this problem. First option is to make your function static. Then you can use it directly without instance. Second option is just call it differently: instantiate class first to get instance, and then call your method on this instance.
And also keep in mind, that await keyword can be used only inside other async function. And you need await keyword if you want to handle promise, returned by async function (it returns promise of result, not result)

Why I need to call every function with async keyword for calling an API in modularized code

I have just started with async and await and trying to convert all my callback syntax to async/await style.
One thing I could not understand is, why I need to every time prefix my function with async keyword.
Here is the example:
APIQuery.js
makeRequest: async(options) => {
try {
var response = await(request(options1))
}
catch(err){
console.log("Some error occurred");
response = undefined;
}
return response;
}
MobileAPI.js
getMobileData: async modal => {
var options = {method: 'GET', json: true,uri: 'https://example.com/mobile/'+modal}
var response = await APIQuery.makeRequest(options);
}
MobileService.js
getMobileDataService: async modal => {
var response = await MobileAPI.getMobileData(modal);
}
MobileController.js
Similarly again I have to use async and await combination to return response.
So my question is, is there a way to get rid of using this everywhere. Calling async await inside APIQuery.js is not enough?
If you want to use the await operator, you have to use async keyword before function declaration:
The await operator is used to wait for a Promise. It can only be used inside an async function.
If you don't want to use async everywhere, you can continue using callbacks or Promises (then, catch, etc.).
So my question is, is there a way to get rid of using this everywhere.
You can't block. Period. As soon as you block, your server would become completely unresponsive to all requests until it's unblocked. For this reason, your code must be asynchronous.
Callbacks are a perfectly valid form of asynchrony, but async/await is easier to use, understand, and maintain.

Resources