Trying to refactor a promisified function in to try-catch block - javascript

I am trying to refactor this code using try-catch blocks:
export const authorizeConnectyCube = async (accessToken) => {
const userCredentials = {
provider: 'firebase_phone',
'firebase_phone[project_id]': "xxxxxxxx",
'firebase_phone[access_token]': accessToken,
};
await createSession();
return new Promise((resolve, reject) => {
ConnectyCube.login(userCredentials, (error, user) => {
user ? resolve(user) : reject(error);
})
}).catch(error => console.log(error));
}
const createSession = () => {
return new Promise((resolve, reject) => {
ConnectyCube.createSession((error, session) => {
session ? resolve(session.user) : reject(error)
})
}).catch(error => console.log(error));
}
However I'm not getting the same result - the asynchronousity seems to be being handled differently. Here is my attempt at refactoring:
export const authorizeConnectyCube = async (accessToken) => {
const userCredentials = {
provider: 'firebase_phone',
'firebase_phone[project_id]': "xxxxxxxxxx",
'firebase_phone[access_token]': accessToken,
};
await createSession();
try {
ConnectyCube.login(userCredentials, (error, user) => {
return user;
})
}
catch (error) {
console.log(error)
}
}
const createSession = () => {
try {
ConnectyCube.createSession((error, session) => {
return session.user
})
} catch (error) {
console.log(error);
}
}
Is there any particular part of what I'm wrong? Thanks.

Callback-based APIs don't readily turn into something you can use for async/await (which under the hood uses promises). You'll have to "promisify" them first (i.e. wrap them in promises).
Here's an example of what I'm trying to say:
// Promisify these callback-based APIs.
const login = userCredentials => {
return new Promise((resolve, reject) => {
ConnectyCube.login(userCredentials, (error, user) => {
user ? resolve(user) : reject(error);
})
})
})
const createSession = () => {
return new Promise((resolve, reject) => {
ConnectyCube.createSession((error, session) => {
session ? resolve(session.user) : reject(error)
})
})
})
// Then use them in an async function
export const authorizeConnectyCube = async (accessToken) => {
const userCredentials = {
provider: 'firebase_phone',
'firebase_phone[project_id]': "xxxxxxxx",
'firebase_phone[access_token]': accessToken,
}
try {
await createSession()
return login(userCredentials)
} catch (e) {
console.warn(e)
}
}
Also, async functions return promises, with the resolved value being the return value, and the rejected value being any uncaught error thrown inside. A value wrapped in a promise as return value for an async function is redundant.
If you're using Node 8+, it has a utility called promisify which accepts a callback-based API and returns a promise-returning version of it.

Related

Getting PostgresSQL 42703 error (invalid column error)

I am working on a React project with PostgreSQL database, this is the first time I am using it, and I am getting 42703 error on querying a particular column.
Below is the code I have written to query
const getList = (userId) => {
return new Promise(function (resolve, reject) {
pool.query(`SELECT items FROM public."user" where id=${userId}`, (error, results) => {
if (error) {
reject(error)
}
resolve(results);
})
})
}
I have defined this getList function and then I am making an api call to call this function by passing the userId like this
app.get(`/expenses`, verifySession(), async (req, res) => {
const userId = req.session.userId;
database.getList(userId)
.then(response => {
res.status(200).send(response);
})
.catch(error => {
res.status(500).send(error);
})
})
I even tried passing the userId directly as shown below , still it gives me the same error , which probably means I am querying in a wrong way
app.get(`/expenses`, verifySession(), async (req, res) => {
//const userId = req.session.userId;
database.getList('17a6dea6-a63e-4da7-9910-df7eddb672e6')
.then(response => {
res.status(200).send(response);
})
.catch(error => {
res.status(500).send(error);
})
})
Only when I directly write the string in the query it works properly like this
const getList = (userId) => {
return new Promise(function (resolve, reject) {
pool.query(`SELECT items FROM public."user" where id='17a6dea6-a63e-4da7-9910-df7eddb672e6'`, (error, results) => {
if (error) {
reject(error)
}
resolve(results);
})
})
}
Can someone please help we with what is exactly going wrong and if my syntax is correct or not ?
This is the frontend part of where I am calling the api.
function getDataForUser() {
fetch(`http://localhost:3001/data`)
.then(response => {
return response.json();
}).then(data => {
console.log(data.rows[0]);
})
}
This problem happened because you didn't use a single quotation for string type in the query. When using where id=${userId} and called with 17a6dea6-a63e-4da7-9910-df7eddb672e6 converted to where id=17a6dea6-a63e-4da7-9910-df7eddb672e6 and this make problem.
You can use two scenarios to handle it:
Use the single quotation for string type:
const getList = (userId) => {
return new Promise(function (resolve, reject) {
pool.query(`SELECT items FROM public."user" where id='${userId}'`, (error, results) => {
if (error) {
reject(error)
}
resolve(results);
})
})
}
Use parameter binding (As default it converted type)
const getList = (userId) => {
return new Promise(function (resolve, reject) {
pool.query(`SELECT items FROM public."user" where id=$1`, [userId], (error, results) => {
if (error) {
reject(error)
}
resolve(results);
})
})
}

async function not getting result

I am trying to get the result of an async function in my calculateOrderAmount function but it returns undefined.
The console.log in the called function returns the good result, but inside calculateOrderAmount, I get undefined. Here is my code:
getMultiStrats = async () => {
await MultiStrats.findOne({}, (err, multiStrats) => {
if (err) {
return err
}
if(!multiStrats) {
return console.log('MultiStrat not found')
}
console.log('returns MultiStrat: ' + multiStrats)
return multiStrats
})
.catch(err => console.log(err))
}
async function calculateOrderAmount(balance, ticker){
const multiState = await StrategyController.getMultiStrats().catch((err) => console.log(err))
console.log('multiState: ' + multiState)
some logic
}
Here is the console log:
multiState: undefined
returns MultiStrat: {
_id: 5ff73c74d1135b39fc709b80,
positionsCount: 1,
inTradeCount: 0,
__v: 0
}
What did I miss? Thanks you very much for your time!
The current approach is pretty unclear--there's no need for .catch, async, await all at once. return multiStrats returns from the inside of the callback, not from getMultiStrats. The async/await on getMultiStrats is superfluous, just adding another promise wrapper that doesn't accomplish anything.
Given that findOne as shown here uses a callback rather than a promise, you can either use callbacks all the way or you can promisify findOne as follows, using .then and .catch in the caller:
const MultiStrats = {
findOne: (obj, cb) => cb(null, "I'm a multistrat!")
};
const StrategyController = {
getMultiStrats: () => new Promise((resolve, reject) =>
MultiStrats.findOne({}, (err, multiStrats) => {
if (err) {
return reject(err);
}
else if (multiStrats) {
return resolve(multiStrats);
}
reject(Error("Multistrat not found"));
})
)
};
const calculateOrderAmount = (balance, ticker) =>
StrategyController
.getMultiStrats()
.then(multiState => {
console.log('multiState: ' + multiState)
// some logic
})
.catch(err => console.error(err))
;
calculateOrderAmount();
Or use async/await and try/catch:
const MultiStrats = {
findOne: (obj, cb) => cb(null, "I'm a multistrat!")
};
const StrategyController = {
getMultiStrats: () => new Promise((resolve, reject) =>
MultiStrats.findOne({}, (err, multiStrats) => {
if (err) {
return reject(err);
}
else if (multiStrats) {
return resolve(multiStrats);
}
reject(Error("Multistrat not found"));
})
)
};
const calculateOrderAmount = async (balance, ticker) => {
try {
const multiState = await StrategyController.getMultiStrats();
console.log('multiState: ' + multiState)
// some logic
}
catch (err) {
console.error(err);
}
};
calculateOrderAmount();
If this is MongoDB's findOne and already returns a promise, then you can just return the promise to the caller, optionally awaiting it and throwing for the null result:
const MultiStrats = {
findOne: async query => "I'm a multistrat!"
};
const StrategyController = {
getMultiStrats: async () => {
const result = await MultiStrats.findOne({});
if (result) {
return result;
}
throw Error("Multistrat not found");
}
};
const calculateOrderAmount = (balance, ticker) =>
StrategyController
.getMultiStrats()
.then(multiState => {
console.log('multiState: ' + multiState);
// some logic
})
.catch(err => console.error(err))
;
calculateOrderAmount();
You cannot return values from an inner callback and reach the outer function, I would suggest
1- only use promises
2- wrap your code with promise to be sure that await will return the async result the way you expect to have.
getMultiStrats = async () => {
return new Promise((resolve, reject) => {
MultiStrats.findOne({}, (err, multiStrats) => {
if (err) {
return err
}
if (!multiStrats) {
console.log('MultiStrat not found')
reject('MultiStrat not found')
}
console.log('returns MultiStrat: ' + multiStrats)
resolve(multiStrats);
})
.catch(err => {
console.log(err);
reject(err)
})
})
}
async function calculateOrderAmount(balance, ticker) {
try {
const multiState = await StrategyController.getMultiStrats()
console.log('multiState: ' + multiState)
// some logic
} catch (error) {
console.error(error);
}
}
Assuming that you use mongoose. I suggest using the promise interface like described in the documentation.
const getMultiStrats = async () => {
const query = MultiStrats.findOne({});
let multiStrats;
try {
multiStrats = await query.exec();
} catch (error) {
return error;
}
if (multiStrats) {
console.log("returns MultiStrat: " + multiStrats);
} else {
console.log("MultiStrat not found");
}
return multiStrats;
}
I would personally not return the error, but instead just let the error be thrown. With the above code the caller of getMultiStrats has to figure out if there return value is the expected result or an error. If you don't catch the error, it is thrown further up to the caller.
const getMultiStrats = async () => {
const multiStrats = await MultiStrats.findOne({}).exec();
if (multiStrats) {
console.log("returns MultiStrat: " + multiStrats);
} else {
console.log("MultiStrat not found");
}
return multiStrats;
}
You can further simplify this if you where to leave the console.log of of the equation.
const getMultiStrats = () => MultiStrats.findOne({}).exec();

How to make two api calls using Promise.all within Angular9?

I making an api call using Promise.all as below:
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
The above code is working fine.
Now I want to make another api call after the successful completion of this.serviceB.retry(oretry).
The second api is this.serviceB.createDbEntry(sentry) and sentry looks as below:
const sretry: SDInterface = {
hostName,
Id: this.Id.slice(0, this.Id.length),
reason: this.reason
};
And, I am doing it as below
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
const sretry: SDInterface = {
hostName,
Id: this.Id.slice(0, this.Id.length),
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
The above code is giving an error:
error: "SequelizeValidationError: string violation: Id cannot be an array or an object"
It is looks like it is not calling the second api for every Id
You may want to take a look a forkJoin
import { Observable, forkJoin } from 'rxjs';
And then
ngOnInit() {
let one = this.http.get('some/api/1') //some observable;
let two = this.http.get('some/api/2') // another observable;
forkJoin([one, tow]).subscribe(response => {
// results[0] is our one call
// results[1] is our second call
let var1 = response[1];
let var2 = response[0];
}/*, error => { in case error handler } */);
}
Wouldn't it be better to use Promise.all() once more?
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
})
.then(() => {
return Promise.all(this.Id.slice(0, this.Id.length).map(id => {
return new Promise((resolve, reject) => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
})
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
And using toPromise() will make the code more concise.
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
return this.serviceB.retry(oretry).toPromise();
})
.then(() => {
return Promise.all(this.Id.slice(0, this.Id.length).map(id => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).toPromise();
})
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
Use combineLatest, in Angular we use RxJs not promises.
combineLatest(
[this.http.get('call1'), this.http.get('call2')]
).subscribe(([result1, result2]) => {
// do stuff with result1 and result2
});
promise.all takes input in an array and gives response in an array,
Create 2 functions each with your asynchronous logic returning a promise,
Say funcA and funcB, then use below to invoke them parellely
Promise.all([funcA(this.hostName), funcB(this.id)])
.then(respones => {
console.log(responses[0]); //return value for funcA
console.log(responses[1]); //return value for funcB
})
.catch(err => console.log(err));
I am assuming your logic of functions are correct, I just copy-pasted from your question and gave them structure
const funcA = (hostName) => {
hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
});
});
}
const funcB = (Id) => {
Id.slice(0, this.Id.length).map(id => {
return new Promise((resolve, reject) => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
})
}

How to properly throw an error from the promise

I have one function, which returns Promise:
updatePassword(currentPassword: string, newPassword: string): Promise<void> {
return this.fireAuth.currentUser.then((user: firebase.User) => {
if (user) {
const credentials = auth.EmailAuthProvider.credential(user.email, currentPassword)
user.reauthenticateWithCredential(credentials).then(res => {
if (res) {
user.updatePassword(newPassword)
}
}).catch(err => {
throw new Error(err)
})
}
})
}
I call it inside another component:
this.userService.updatePassword(currentPassword, newPassword).then(() => {
console.log('successfully')
}).catch(err => {
console.log('error')
})
But even when updatePassword() return Error, the function call in component still console log 'successfully' from 'then`. How to properly throw an error in my case?
You also need to return your inner promise. Like this:
updatePassword(currentPassword: string, newPassword: string): Promise<void> {
return this.fireAuth.currentUser.then((user: firebase.User) => {
if (user) {
const credentials = auth.EmailAuthProvider.credential(user.email, currentPassword)
return user.reauthenticateWithCredential(credentials).then(res => {
if (res) {
user.updatePassword(newPassword)
}
}).catch(err => {
throw new Error(err)
})
}
throw new Error('USER_NOT_FOUND')
})
}
Edit:
I also added throw if you do not get user as safety net.
Refactor your function to use async/await, and you don't need to manually throw anything.
You may wish to make those return falses some sort of throw too.
async updatePassword(currentPassword: string, newPassword: string): Promise<void> {
const user = await this.fireAuth.currentUser;
if(!user) return false;
const credentials = auth.EmailAuthProvider.credential(user.email, currentPassword);
const res = await user.reauthenticateWithCredential(credentials);
if(!res) return false;
user.updatePassword(newPassword);
return true;
}
If your fireAuth call fails, then throw a Error from the updatePassword catch block.
Check the working snippet attached.
let fireAuth = (pass = false) => {
if (pass) return Promise.resolve("passed");
return Promise.reject("failed");
};
function updatePassword(shouldPass = false) {
if (!shouldPass) {
return fireAuth(false)
.then(console.log)
.catch((err) => {
console.log('err in fireauth', err)
throw new Error('OOPS')
});
}
return Promise.resolve("success");
}
let failedResult = updatePassword()
.then()
.catch((err) => console.log("failedresult error", err.toString()));
let successResult = updatePassword(true)
.then((res) => console.log("res is", res))
.catch();

Why is the result of await/async undefined?

I have a route like:
router.get("/my_resource", myController.getResult);
Then my controller is like:
getResult: async (req, res, next) => {
try {
const param = req.query.param;
let response = {};
if (param) {
let [result1, result2] = await Promise.all([myModel.getResult1(param), myModel.getResult2(param)]);
console.log(result1);
console.log(result2);
}
res.send(response);
} catch (e) {
next(e);
}
}
And my model (which is querying a mongodb) looks like:
getResult1: (param) => {
new Promise((resolve, reject) => {
MongoSchema1.findById(param, (error, result) => {
if (error) return reject(error);
resolve(result ? result : {});
}).select("field1 field2");
})
}
getResult2: (param) => {
new Promise((resolve, reject) => {
MongoSchema2.findById(param, (error, result) => {
if (error) return reject(error);
resolve(result ? result : {});
}).select("field1 field2");
})
}
Here, when I try to console.log() in the model functions I can see that the result is not undefined. I get the correct result. However, I can see that the console.log() in the controller method gets executed before the console.log() in the model methods.
In the code above, I'm making the async calls in parallel with Promise.all(). However, even if I try to run one at a time, I still get my result to be undefined in the controller method. What am I doing wrong? Do I need import any module before I can await? Thanks.
The problem is that your methods getResult1 and getResult2 do not return Promises objects.
Replace
getResult1: (param) => {
new Promise((resolve, reject) => {
MongoSchema1.findById(param, (error, result) => {
if (error) return reject(error);
resolve(result ? result : {});
}).select("field1 field2");
})
}
By
getResult1: (param) => {
return new Promise((resolve, reject) => {
MongoSchema1.findById(param, (error, result) => {
if (error) return reject(error);
resolve(result ? result : {});
}).select("field1 field2");
});
}
Or
getResult1: param => new Promise((resolve, reject) => {
MongoSchema1.findById(param, (error, result) => {
if (error) return reject(error);
resolve(result ? result : {});
}).select("field1 field2"));
}
Or
getResult1: async param => (await MongoSchema1.findById(param).select('field1 field2')) || {};
Or
getResult1: async param => (await MongoSchema1.findById(param, 'field1 field2')) || {};

Categories