I am a newbie in node.js environment. I read a lot of source about implementing Promises and chaining them together. I am trying to avoid anti-pattern implementation but I could not figure it out how can I do it.
There is a User Registration flow in the system.
First, I check the username.
if there is no user in the DB with this username I create a User model and save it to the DB.
Can you kindly see my comment inline ?
app.js
RegisterUser("existUser","123").then(user=>{
//send response, etc
}).catch(er => {
console.log(er);
//log error,send proper response, etc
// Should I catch error here or inner scope where RegisterUser implemented ?
});
userService.js
function RegisterUser(username, password) {
return new Promise((resolve, reject) => {
GetUser(username)
.then(user=>{
if(user)reject(new Error("User Exists"));
else{
resolve(SaveUser(username,password))// Is that ugly?
//what if I have one more repository logic here ?
//callback train...
}
})
.then(user => {
resolve(user);//If I do not want to resolve anything what is the best practice for it, like void functions?
}).catch(err=>{
console.log(err); // I catch the db error what will I do now :)
reject(err);// It seems not good way to handle it, isn't it ?
// Upper promise will handle that too. But I dont know how can I deal with that.
});;
});
}
repository.js
function GetUser(username) {
return new Promise((resolve, reject) => {
if (username === "existUser")
resolve("existUser");
else resolve("");
});
}
function SaveUser(username, password) {
return new Promise((resolve, reject) => {
reject(new Error("There is a problem with db"));//assume we forgot to run mongod
});
}
The code above seems awful to me.
I thought I need to define some method that can chain after GetUser method.
like
GetUser(username)
.then(SaveUserRefined)// how could it know other parameters like password, etc
.then(user=> {resolve(user)}) // resolve inside then ? confusing.
.catch(er=>//...);
I feel I do anti-pattern here and create "promise hell"
How could a simple flow like that implemented.
Validate username and save it.
Thanks.
Yes, that's the Promise constructor antipattern! Just write
function registerUser(username, password) {
return getUser(username).then(user => {
if (user) throw new Error("User Exists");
else return saveUser(username,password)
});
}
Notice I also lowercased your function names. You should only capitalise your constructor functions.
Should I catch error here or inner scope where registerUser implemented?
You should catch errors where you can handle them - and you should handle errors at the end of the chain (by logging them, usually). If registerUser doesn't provide a fallback result, it doesn't need to handle anything, and it usually also doesn't need to log the error message on its own (if you want to, see here for an example).
See also Do I always need catch() at the end even if I use a reject callback in all then-ables?.
If you already are working with promises, then there's no need to create your own. When you call .then on a promise and provide a function saying what to do, that creates a new promise, which will resolve to whatever the function returns.
So your registerUser function should look something like this:
function registerUser(username, password) {
return getUser(username)
.then(user => {
if (user) {
throw new Error('User Exists');
}
return saveUser(username, password)
});
}
and you use it like this:
registerUser('starfox', 'doABarrelRoll')
.catch(error => console.log(error);
Note that if SaveUser causes an error, it will end up in this final .catch, since i didn't put any error handling inside registerUser. It's up to you to decide where you want to handle the errors. If it's something recoverable, maybe registerUser can handle it, and the outside world never needs to know. But you're already throwing an error if the user name exists, so the caller will need to be aware of errors anyway.
Additionally your getUser and saveUser functions might also be able to avoid creating their own promises, assuming the real implementation calls some function that returns a promise.
Your should use async/await syntax to avoid Promise Hell.
Change your code like this
/**
* This is a promise that resolve a user or reject if GetUser or SaveUser reject
*/
async function RegisterUser (username, password) {
var user = await GetUser(username)
if (user)
return user;
var userSaved = await SaveUser(username,password)
return userSaved
}
If you use RegisterUser inside a async function just code
async function foo () {
try {
var usr = await RegisterUser('carlos', 'secret123')
return usr
} catch (e) {
console.error('some error', e)
return null
}
}
Or if you use it like a promise
RegisterUser('carlos', 'secret123')
.then(usr => console.log('goood', usr))
.catch(e => console.error('some error', e))
Related
Is it bad practice to nest multiple then functions? It seems fairly logical to say "execute this function, and when it's done, execute this one" (and so on) but the code looks horrible.
If it helps I originally had this query in the context of firestore getting user details then getting documents
firebaseApp.auth().signInWithEmailAndPassword(email, password).catch(function(error) {
//If error
}).then(()=>{
firebaseApp.firestore().collection(collectionName).where("associatedID", "==", authID).get().then((snapshot)=>{
snapshot.docs.forEach(doc => {
//Do stuff with data that we've just grabbed
})
}).then(()=>{
//Tell the user in the UI
});
});
Are there alternatives? One that springs to mind is like so
var functionOne = () =>{
console.log("I get called later");
}
var promise1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 3000);
});
promise1.then(function(value) {
functionOne();
});
But even then it seems like it could get complex after a few .then()'s
Return the Promise from the first outer .then, and then use the resolve value in a second outer .then, without any nested .thens:
firebaseApp.auth().signInWithEmailAndPassword(email, password)
.then(()=>{
return firebaseApp.firestore().collection(collectionName).where("associatedID", "==", authID).get()
})
.then((snapshot) => {
snapshot.docs.forEach(doc => {
//Do stuff with data that we've just grabbed
});
//Tell the user in the UI
})
.catch((error) => {
// handle errors
});
Make sure not to catch too early - if there's an error anywhere in the chain, often you'll want to stop normal execution and go directly to the end (eg, tell the user that there was an error).
If you're worried about code readability, consider using async/await (and transpile down your production code for older browsers):
// in an async function:
try {
await firebaseApp.auth().signInWithEmailAndPassword(email, password);
const snapshot = await firebaseApp.firestore().collection(collectionName).where("associatedID", "==", authID).get()
snapshot.docs.forEach(doc => {
//Do stuff with data that we've just grabbed
});
//Tell the user in the UI
} catch(error) {
// handle errors
}
It depends on what you want to do: If you need access both to the result passed into then and to the result of a subsequent operation you're doing within the then at the same time, nesting is reasonable:
doSomething()
.then(result1 => {
return doSomethingElse()
.then(result2 => {
return result1 + result2;
});
})
.then(combinedResult => {
// Use `combinedResult`...
})
.catch(/*...*/);
often, though, you just need to pass a single value through the chain, by returning the promise from your subsequent operation from the then handler:
doSomething()
.then(result => {
return doSomethingElse(result);
})
.then(lastResult => {
// `lastResult` is the fulfillment value from `doSomethingElse(result)`
})
.catch(/*...*/);
Doing that resolves the promise then created to the promise returned by get() on the query. (To "resolve a promise to something" means that you've made the promise's settlement depend on the thing you've resolved it to. If you resolve it to another promise, its settlement depends on the settlement of that other promise.)
Looking at your Firebase example, I'd probably do it without nesting:
firebaseApp.auth()
.signInWithEmailAndPassword(email, password)
.then(() => firebaseApp.firestore().collection(collectionName).where("associatedID", "==", authID).get())
.then((snapshot) => {
snapshot.docs.forEach(doc => {
// Do stuff with data
});
})
.then(() => {
// Tell the user in the UI
})
.catch(function(error) {
// Handle/report error, which may be from `signInWithEmailAndPassword`, your collection query, or an error raised by your code in the `then` handlers above
});
You should chain promises and, also, you can name the functions, which IMHO can improve readibility significantly. Consider something like this
const signIn = () => firebaseApp.auth().signInWithEmailAndPassword(email, password);
const onSigninError = (err) => // error handling logic here
const getCollection = () => firebaseApp.firestore().collection(collectionName).where("associatedID", "==", authID)
.get();
const processSnapshot = (snapshot) => snapshot.doc.forEach(// do stuff here
const displayMessage = () => // do stuff here
signIn()
.catch(onSigninError)
.then(getCollection)
.then(processSnapshot)
.then(displayMessage);
CertainPerformance highlighted in my previous post advised me to avoid the explicit Promise construction antipattern with reference to to following question in stackoverflow
Frankly, Speaking, I am new to JS and node and I haven't used promise a lot. I went and read those article but either I was unable to comprehend or unable to relate or maybe somewhere my understanding of promises have been vague/wrong all together
So I decided to ask this question in a new thread and seek for help.
So what am I doing and why am I doing it
I am creating helper/common function which I could use to keep my code tidy and if in case I want to change anything inside function at anytime, I don't have to manually change every function.
So these are the functions I have made
//Find user by email Address
const findUserByEmail = (emailAddress) => {
return new Promise((resolve, reject) => {
User.findOne({email: emailAddress}).then(response => {
resolve(res)
}).catch(error => {
reject("Error in findUserByEmail", error);
})
})
}
//Create User
const createNewUser = (newUserDetails) => {
return new Promise((resolve, reject) => {
new User({
fullName: newUserDetails.fullName,
email: newUserDetails.email,
image: newUserDetails.image,
gender: newUserDetails.gender,
age: newUserDetails.age
}).save().then((response) => {
resolve(response)
}).catch((error) => {
reject("Problem in Creating New User", error)
})
})
}
Question 1
Now, I am assuming CertainPerformance said the excessive use of promises because I am creating new promise return new Promise((resolve, reject) => { when I am already using promises with mongoose User.findOne({email: emailAddress}).then(response => { ?
But the reason for me to create those promise was, when I call these helper function from anywhere in my app after importing
const { findUserByEmail } = require("./my_db_query");
I would probably want it return a response or throw an error in case of error
findUserByEmail("test#example.com").then(/*...*/).catch(/*...*/);
If I change my above code snippet without adding new promise
function findUserByEmail (email) {
return User.findOne({email: email}).then(currentUser => currentUser).catch(error => error)
}
Question 2
Then I won't probably be able to .then and .catch in findUserByEmail("test#example.com")?
And In API route of App, where I would be calling the findUserByEmail("test#example.com") function, I would want to do something else if there is an error (which would be different for different case and hence I cannot use it in my helper function).
Question 3
Does, it make sense now for doing return new Promise((resolve, reject) => { instead of doing just one return User.findOne( or am I missing something?
Because .findOne already returns a Promise, there's no need to construct a new one with new Promise - instead, just chain onto the existing Promise chain with .then and .catch as needed. Such Promise chains can have any number of .thens and .catchs - just because you consume a Promise with one .then doesn't prevent you from using the same resolve value elsewhere. To illustrate:
makePromise()
.then((result) => {
console.log(result);
// Returning inside a `.then` will pass along the value to the next `.then`:
return result;
})
.then((result) => {
// this `result` will be the same as the one above
});
In other words - there's no need to construct a new Promise every time you want to be able to use another .then. So:
Then I won't probably be able to .then and .catch in findUserByEmail("test#example.com")
isn't correct - you can indeed chain onto the end of an existing Promise with as many .thens and .catches as you want.
Note that a .then which only returns its parameter and does nothing else (such as .then(currentUser => currentUser)) is superfluous - it won't do anything at all. Also note that a .catch will catch Promise rejections and resolve to a resolved Promise. So if you do
function findUserByEmail(email) {
return User.findOne({email: email})
.then(currentUser => currentUser)
.catch(error => error)
}
that catch means that callers of findUserByEmail will not be able to catch errors, because any possible errors were caught in findUserByEmail's catch. Usually, it's a good idea to allow errors to percolate up to the caller of the function, that way you could, for example:
someFunctionThatReturnsPromise('foobar')
.then((result) => {
// everything is normal, send the result
res.send(result);
})
.catch((err) => {
// there was an error, set response status code to 500:
res.status(500).send('there was an error');
})
So, unless your findUserByEmail or createNewUser helper functions need to do something specific when there's an error, it would probably be best just to return the Promise alone:
const findUserByEmail = email => User.findOne(email);
const createNewUser = newUserDetails => new User(newUserDetails).save();
If your helper functions do need to do something when there's an error, then to make sure that the error gets passed along properly to the caller of the function, I'd recommend either throwing the error inside the catch:
const findUserByEmail = email => User.findOne(email)
.catch((err) => {
// error handling - save error text somewhere, do a console.log, etc
throw err;
});
so that you can catch when something else calls findUserByEmail. Otherwise, if you do something like
const findUserByEmail = email => User.findOne(email)
.catch((err) => {
// do something with err
return err;
});
then the caller of findUserByEmail will have to check inside the .then if the result is actually an error, which is weird:
findUserByEmail('foo#bar.com')
.then((result) => {
if (result instanceof Error) {
// do something
} else {
// No errors
}
});
Better to throw the error in findUserByEmail's catch, so that the consumer of findUserByEmail can also .catch.
It never makes sense to create a promise with promise constructor when there's existing promise, that's why it's called promise construction antipattern.
This is a mistake, reject("Error in findUserByEmail", error). reject accepts only 1
argument, which is rejection reason. error will be ignored. It's conventionanl for an error to be Error object and not a string.
The function may be refactored to:
const findUserByEmail = (emailAddress) => {
return User.findOne({email: emailAddress})
.then(response => response) // noop
.catch(error => {
const readableError = new Error('Error in findUserByEmail');
readableError.originalError = error;
throw readableError;
});
})
}
etc.
Antipatterns don't necessary result in bad performance but they result in code smell. They make the code harder to read, maintain and test, also show that a developer may have a poor understanding of the subject.
Promise constructor has some insignificant performance impact. It introduces another level of nesting and contributes to callback hell - promises are supposed to help avoiding it.
If I change my above code snippet without adding new promise <...>
Then I won't probably be able to .then and .catch in findUserByEmail("test#example.com")?
No, a promise can be chained with then(...) and catch(...) (which is syntactic sugar for then(null, ...)) as many times as needed, that's the strong side of the pattern. Notice that catch(err => { return err }) and catch(err => { throw err }) is not the same thing, the former catches an error, the latter rethrows it.
Like the title says, it's clear based on running authHandler (see below) that it's not doing what I want it to. But I don't know why.
Basically, what I was wanting authHandle to do is first register a new user, generate a userUID and token, and subsequently update the state. Then, once that has been achieved, run the second half of the code to store the new user's user name.
Right now. All it does is registering the user (so initiating the first half of the code) but not executing the second .then() half.
I suspect it's something to do with the states between the first half and second half of authHandler.
I'm hoping someone can help me figure out where I went wrong ><
authHandler = () => {
return new Promise ((resolve, reject) => {
const authData = {
email: this.state.controls.email.value,
password: this.state.controls.password.value
};
this.props.onTryAuth(authData, this.state.authMode); // registers a new user and gives a userUID and token
})
.then(() => {
this.props.onAddUserData(
this.state.controls.userName.value,
)
}) //store the username of the new user
.catch(err => {
console.log(err);
alert("Oops! Something went wrong, please try again")
})
};
You need to either resolve or reject the promise you're creating - right now it's stuck in a pending state so whatever gets called in the then of authHandler() will never happen. You should also be returning the promise, calling then and catch where you do won't work properly. this code snippet reorganizes it in a way that should work.
authHandler = () => {
return new Promise ((resolve, reject) => {
try {
const authData = {
email: this.state.controls.email.value,
password: this.state.controls.password.value
};
this.props.onTryAuth(authData, this.state.authMode); // registers a new user and gives a userUID and token
this.props.onAddUserData(this.state.controls.userName.value)
resolve('done')
} //store the username of the new user
catch(err){
console.log(err);
alert("Oops! Something went wrong, please try again")
reject(err)
}
})
};
Using sequelize.js in a nodejs app, and I have a promise.all that takes two promises (a user query, and a color query):
router.get(`/someEndPoint`, (req, res) => {
let userAccount = user.findOne({
where: {
id: //some ID
}
});
let colorStuff = color.findOne({
where: {
colorName: //some color
}
})
Promise.all([userAccount , colorStuff ]).then(([result1, result2]) => {
//do stuff, such as:
res.send('success');
}).catch(err => {
console.log(err)
});
});
At the part that says //do stuff, my console keeps giving me this warning:
a promise was created in a handler at... but was not returned from it,
see (URL that I can't post) at Function.Promise.attempt.Promise.try
I'm not sure how to resolve this. I thought after the .then that the promises are resolved?
Hard to tell without other context, but perhaps you need to return the Promise.all
return Promise.all([user, color])...
From the bluebird docs here: https://github.com/petkaantonov/bluebird/blob/master/docs/docs/warning-explanations.md#warning-a-promise-was-created-in-a-handler-but-was-not-returned-from-it
if there are any other promises created in the // do stuff area, be sure to return those as well.
I'm curerntly playing with Typescript and ionic and also new to async-await on javascript. I'm trying to get a good undestanding of promises but I can't figure out how to wrap methods which call multiple promises to return a promise. I'll try to elaborate better:
Basically, I have an object which can create a SQLite database, and when the create() method is invoked, it would return a Promise with the actual database object once it has been created.
Then, when the promise resolves and the DB object is returned, I need to use it to execute some statements in a transaction to create all the tables, and a new promise is returned when I invoke the execution of all the statements in the transaction.
Then when the transaction is finished and everything went good, I need to assign the database object to a class property and set a flag indicating database is created and ready to go.
So I thought it would be a good idea to wrap this database initialization stuff in a method called createDatabase() or something like that, which returns a Promise<SQLiteObject>, where SQLiteObject represents the database. This method would be called on initialization and should return the SQLiteObject representing the database once everything went OK, or throw an error which I would log in the .catch() method of the Promise.
I have a basic understanding of promises and how to use then() and catch() methods but I'm a bit confused when I have to create the database, then do something else when the promise resolves, and when everything is done, return a Promise containing the DB object, which is an instance of the class SQLiteObject.
CODE
Below is my current Typescript code. It's not valid typescript as I don't know how to return the Promise<SQLiteObject> from the async function.
async createDatabase(): Promise<SQLiteObject> {
this.sqlite.create({
name: this.dbName,
location: this.dbLocation
}).then( (db: SQLiteObject) => {
// Insert all tables.
let createTableParam: string = `CREATE TABLE IF NOT EXISTS param (name PRIMARY KEY NOT NULL, value TEXT)`;
let createTableNews: string = `CREATE TABLE IF NOT EXISTS news (id PRIMARY KEY NOT NULL,title TEXT,
content TEXT, image TEXT, date DATE)`;
db.transaction(tx => {
tx.executeSql(createTableParam);
tx.executeSql(createTableNews);
// Add here more tables to create if needed
}
)
.then( () => {
console.log('Tables were created');
this.isActive = true;
})
.catch(error => {
console.log(`Error creating tables - ${error}`);
});
}).catch(
error => console.log(`Error at SQLite initialization - ${error}`)
);
}
RESEARCH SO FAR
I have read this question here which seems related but I am
confused on how to implement on my code
Checked async/await on MDN
This question here also seems related but still don't really understand how to do it.
Typescript promises at "Typescript Deep Dive"
Async/Await at "Typescript Deep Dive"
You used async, so that means you can use await inside the function any time you have a Promise and write the code almost as though it were synchronous.
async createDatabase(): Promise<SQLiteObject> {
let db: SQLiteObject;
try {
db = await this.sqlite.create({
name: this.dbName,
location: this.dbLocation
});
} catch(error) {
console.log(`Error at SQLite initialization - ${error}`);
return;
);
// Insert all tables.
let createTableParam: string = `CREATE TABLE IF NOT EXISTS param (name PRIMARY KEY NOT NULL, value TEXT)`;
let createTableNews: string = `CREATE TABLE IF NOT EXISTS news (id PRIMARY KEY NOT NULL,title TEXT,
content TEXT, image TEXT, date DATE)`;
try {
await db.transaction(tx => {
tx.executeSql(createTableParam);
tx.executeSql(createTableNews);
// Add here more tables to create if needed
}
);
console.log('Tables were created');
this.isActive = true;
return db;
catch(error) {
console.log(`Error creating tables - ${error}`);
});
}
Without the await you need to be sure to return that initial promise.
return this.sqlite.create({...
and then again further down you can return the db object:
this.isActive = true;
return db;
Also you should avoid nesting the .then() handlers: when you get another promise just return it from the first handler and chain another .then on the end:
createDatabase(): Promise<SQLiteObject> {
let database: SQLiteObject = null;
return this.sqlite.create({
name: this.dbName,
location: this.dbLocation
})
.catch(error => console.log(`Error at SQLite initialization - ${error}`))
.then( (db: SQLiteObject) => {
// Insert all tables.
let createTableParam: string = `CREATE TABLE IF NOT EXISTS param (name PRIMARY KEY NOT NULL, value TEXT)`;
let createTableNews: string = `CREATE TABLE IF NOT EXISTS news (id PRIMARY KEY NOT NULL,title TEXT,
content TEXT, image TEXT, date DATE)`;
database = db;
return db.transaction(tx => {
tx.executeSql(createTableParam);
tx.executeSql(createTableNews);
// Add here more tables to create if needed
}
);
})
.then( () => {
console.log('Tables were created');
this.isActive = true;
return database;
})
.catch(error => console.log(`Error creating tables - ${error}`));
It appears you're not resolving the promise.
A promise must be resolved or rejected so a async function is capable of responding with a value.
From TypeScript Deep Dive:
const promise = new Promise((resolve, reject) => {
resolve(123);
});
promise.then((res) => {
console.log('I get called:', res === 123); // I get called: true
});
promise.catch((err) => {
// This is never called
});
So in my opinion you should create a promise, and when the DB is created and everything resolve it, if there's a problem with the DB creation, reject it.
Remember you can chain promises, so you could chain them as you create your database.
From TypeScript Deep Dive:
The chain-ability of promises is the heart of the benefit that promises provide. Once you have a promise, from that point on, you use the then function to create a chain of promises.
Check this URL for more info about promises
Hope it helps! :)