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
Related
I am trying out NestJS for the first time.
The question is simple, when I DO NOT use async await in my controller, I am able to return the data without await as async/await is used in the repository class methods
#Get('/:id')
getMessage(#Param('id') id: string) {
const messageId = id;
// works just fine 👇
const message = this.messagesService.findOne(messageId);
return message;
}
But when I make use of NotFoundException from NEST to make sure if I found the data I am supposed to return, I am forced to use async/await because without it, it considers the message to be always there. Which I am assuming is a Promise.
#Get('/:id')
async getMessage(#Param('id') id: string) {
const messageId = id;
// 👇 await
const message = await this.messagesService.findOne(messageId);
if (!message) {
throw new NotFoundException('Message with ID not found');
}
return message;
}
And if I do not use await, it does not throw an exception.
The question is, why/how does it work in the first example without the use of await
The await keyword returns a Promise. Therefore if you return a Promise you have satisfied the contract of returning a Promise.
I presume that Nest.js repository methods need to return a Promise. You have two choices. Either use the async keyword or return a Promise. In the first example you have returned a Promise so that is why it works.
Note that you don't need to use async if you don't want to. You can always go old school. This is what your first example would be like with the logic to check the message:
#Get('/:id')
getMessage(#Param('id') id: string) {
const messageId = id;
// works just fine 👇
const promise = this.messagesService.findOne(messageId).then((message) => {
if (!message) {
throw new NotFoundException('Message with ID not found');
}
else {
return message;
}
});
return promise;
}
We know that in JS, program runs synchronously and findOne is a webAPI provided by the browser. As it is a webapi it will first send the line
const message = this.messagesService.findOne(messageId);
to the api and will return that function again to stack once all the data is received.
(Assuming you know how the event loop and api works in JS)
In the first function you are not checking the variable message(if it is true or false) you are just returning the value so it will return only if the value is present.
But in second function you are checking the var message, if that line is written without the await it will directly go to the "if" statement even before the data is received from the api(findOne) at that time message var would still be undefined. So once you write await, stack will not go to the next line unless the answer from api is received, and at time your if statement will be checked perfectly.
The answer to your question is in the architecture of nestjs/node itself. If you return a promise (your first case) it resolve it and then returns the value. To get more clear idea about this check jiripospisil on 3 Aug 2018 on this issue.
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')
}
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
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).
I am using node 8.x. Hence, I have access to all latest features i.e. async/ await etc.
The scenario is similar to following
(Not correct syntax, just for explanation):
createUser()
{
let userAddress = createAddress(); // Aync, returns a promise
User.create(name: 'foo', address: userAddress); // User creation is dependant on address. Also, User.create returns a promise.
}
Basically, User object creation is dependant on the creation of address object. I want the createUser function to execute asynchronously i.e. to return a promise as soon as possible without waiting for address object to be created.
The purpose of this question is not to get things done but to understand what is the best way to solve this kind of issues in async programming.
Few ways that I can think of:
1: Create a new promise object and return right away when entered in createUser function. Resolve it when the user object is created.
2: Make createUser an async function and then return user Promise. Facing issues with this method:
async function createUser()
{
address = await createAddress();
return User.create(name: 'foo', address: userAddress);
}
The problem is function waits for the address before returning the control which I don't want. (Or it does not make any diff as the function is async. Performance is a big criterion in my case)
How to deal with this kind of promise dependancy where you want to return promise of parent object but you parent object is dependant on child object's promise.
Thank you.
You can write your function as-is and then it depends on how it's called. You can't use await because that blocks in the current scope. You can introduce a new scope using .then, though:
async function createUser() {
address = await createAddress();
return User.create({ name: 'foo', address });
}
...
createUser().then(() => { /* handle user creation */ });
// code here will not wait for `createUser` to finish
Make createUser an async function and then return user Promise. The problem is function waits for the address before returning the control which I don't want.
No, it doesn't. An async function does immediately return a promise to its caller. It waits only when executing the code in its body (and resolves the promise when you return). Your code works, this is exactly what you should be doing.
If createAddress is a promise returning the address, let's say address, you can do something like this:
createAddress().then(function(address) {
User.create(name: 'foo', address: address).then(function() {
// User and address has been created
})
})
// Do something here that runs even though createAddress has not yet been resolved
This will require you to wait for the promise to be resolved before doing some other code.
If you want to return a promise, then you're basically looking to use promises the way they were intended to be used:
createUser() {
return createAddress()
.then((address) =>
User.create({ name: 'foo', address: address })
);
}
Now the createUser method is returning a promise, which will be resolved at some point in the future. Your code can continue work, can add more .then(...) or .catch(...) callbacks to that promise, etc.
This is another approach, assuming that createAddress returns a promise (of course)
function createUser(){
return createAddress().then(function(address) {
// the return is to handle latter the user creation
return User.create(name: 'foo', address: address)
}, function() {
throw 'Promise Rejected';
});
}
createUser().then(function(result) {
console.log("result => " + result);
// Do something here with the 'user creation' if you want
}).catch(function(error) {
console.log("error => " + error);
});
// Do some stuff, while the code above is executed