How to properly throw an error from the promise - javascript

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();

Related

Function does not return true even if it goes through

I can't seem to make this work but I want to return true every-time the function executes successfully, which in this case is "Changing the password".
async function ChangePassword(data) {
const auth = getAuth();
let res = false;
const credential = EmailAuthProvider.credential(
auth.currentUser.email,
data.oldPassword
);
reauthenticateWithCredential(auth.currentUser, credential)
.then(() => {
updatePassword(auth.currentUser, data.password)
.then(() => {
toast.success("Password changed successfully");
res = true;
console.log("res ",res);
})
.catch((error) => {
toast.error(error.message);
});
})
.catch((error) => {
toast.error(error.message);
});
return res;
}
The res variable when called by other functions always results in false even though I see the toast message "Password changed successfully".
async function changePassword() {
if (password === "" || confirmPassword === "" || oldPassword === "") {
toast.error("Please fill all the fields");
} else if (password !== confirmPassword) {
toast.error("Passwords do not match");
} else {
let data = { oldPassword, password };
await ChangePassword(data).then((res) => {
if (res === true) {
setChangePasswordModal(false);
setOpen(false);
}
});
}
}
The if condition in above code never executes because res is always false. I know it has something to do with async await but can't figure out how to make it work
async function ChangePassword(data) {
const auth = getAuth();
let res = false;
const credential = EmailAuthProvider.credential(
auth.currentUser.email,
data.oldPassword
);
reauthenticateWithCredential(auth.currentUser, credential)
.then(() => {
updatePassword(auth.currentUser, data.password)
.then(() => {
toast.success("Password changed successfully");
res = true;
console.log("res ",res);
})
.catch((error) => {
toast.error(error.message);
});
})
.catch((error) => {
toast.error(error.message);
});
return res;
}
Has many logical errors in it.
First you should decide whether you're going to use async and its feature await or classical Promise .thenable -style. Please do not use both, it will only confuse you and the reader.
Let's ditch the async (since you don't even use await), and Promises are chainable, you do not (and MUST not) nest .then blocks, use this instead:
function ChangePassword(data) {
const auth = getAuth();
const credential = EmailAuthProvider.credential(auth.currentUser.email, data.oldPassword);
return reauthenticateWithCredential(auth.currentUser, credential)
.then(() => {
return updatePassword(auth.currentUser, data.password)
})
.then(() => {
toast.success("Password changed successfully");
return true;
})
.catch((error) => {
toast.error(error.message);
return false;
})
}
The key here is that ChangePassword itself returns a Promise.*
The caller is then responsible to call it with .then or use async in combination with await:
ChangePassword({/* data */}).then((result) => {
console.log("ChangePassword done", result)
})
The same code looks a lot cleaner if you use async:
async function ChangePassword(data) {
const auth = getAuth();
const credential = EmailAuthProvider.credential(auth.currentUser.email, data.oldPassword);
try {
await reauthenticateWithCredential(auth.currentUser, credential);
await updatePassword(auth.currentUser, data.password);
toast.success("Password changed successfully");
return true;
} catch (error) {
toast.error(error.message);
return false;
}
}
(If you were wondering how that would look like).
*a function tagged as async ALWAYS returns a promise, by the way.

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 add a new then after fetch has run

I have a method that runs a fetch request and then saves the result or error like this:
saveTema() {
this.gateway.editTema(this.state.tema)
.then(tema => {
this.setState({
tema,
error: null,
isDirty: false,
});
})
.catch(httpOrOtherError => {
if (httpOrOtherError.status) {
if (httpOrOtherError.status === 400) {
httpOrOtherError.json().then(result => {
const serverValidationfailures =
this.transformValideringsfeil(result.valideringsfeil);
this.setState({
error: {
valideringsfeil: {...serverValidationfailures},
},
showActivationDialog: false,
})
});
} else {
this.setState({
error: {httpError: {status: httpOrOtherError.status, statusText: httpOrOtherError.statusText}},
showActivationDialog: false,
});
}
} else {
this.setState({
error: {fetchReject: {message: httpOrOtherError.message}},
showActivationDialog: false,
})
}
})
}
And this is the fetch request itself:
editTema(tema) {
return fetch(
this.temaUrl(tema.id),
{
method: 'PUT',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(tema)
})
.then(res => {
if (res.ok) {
return res.json();
}
throw res;
}
);
}
I would like to run this method from another one, and check if everything went ok with this method and based on that do further actions. Something like this:
this.saveTema().then(() => {
this.props.history.push({
pathname: '/tema',
state: {
successMessage: `Tema ${this.state.tema.id} ble oppdatert`,
}
}}));
But, this is of course wrong, I am not sure how can I do this, to run some code after the fetch handling of the fetch request has finished. What is the right way to do it?
saveTema() {
return this.gateway.editTema(this.state.tema)
...
Return the promise and then you'll be able to do exactly what you are trying to do.
Return the editThema result after setting up the handlers:
saveTema() {
let prom = this.gateway.editTema(this.state.tema)
prom.then(tema => {
// .. success handling code
})
.catch(httpOrOtherError => {
// .. error handling code
})
return prom;
}
Now you can call your function exactly like you wanted to.
You can achieve that by two approaches
Using async/await
Using native Promise
1. async/await way
userController.js
const userUtils = require('./userUtils');
const userCtr = {};
userCtr.searchUser = async (req, res) => {
try {
const { userName } = req.query;
const result = await userUtils.searchUser(userName);
return res.status(200).json(result);
} catch (err) {
return res.status(err.code).json({ error: err.error });
}
};
module.exports = userCtr;
userUtils.js
const userUtils = {};
userUtils.searchUser = async (userName) => {
try {
if (userName) {
// ...Do some cool stuff
const result = [];
return result;
}
const errorObj = { code: 400, error: 'ERR_VALID_PARAM' };
throw errorObj;
} catch (err) {
console.error(err);
throw err;
}
};
module.exports = userUtils;
2. Promise way
userController.js
const userUtils = require('./userUtils');
const userCtr = {};
userCtr.searchUser = (req, res) => {
const { userName } = req.query;
userUtils.searchUser(userName)
.then((result) => {
return res.status(200).json(result);
})
.catch((err) => {
return res.status(err.code).json({ error: err.error });
});
};
module.exports = userCtr;
userUtils.js
const userUtils = {};
userUtils.searchUser = (userName) => {
return new Promise((resolve, reject) => {
if (userName) {
// ...Do some cool stuff
const result = [];
return resolve(result);
} else {
const error = { code: 400, error: 'Please provide valid data!' }
return reject(error);
}
});
};
module.exports = userUtils;
In both approaches you can hold further execution (in both approach Promise are used directly or indirectly), In a second approach you can achieve by .then().catch() whereas in the first approach just you need to put a keyword await and put async on your function, I suggest you to use async/await. Because when you need to wait for the completion of more than 3 promises and yo go with Native Promise then your code will be so messy like .then().then().then() Whereas in a first approach you just need to put a keyword await on starting of your function, Using async/await approach your code will neat and clean and easily understandable and easy to debug.

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

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.

Mocha test if the Http Request Promise throw: Unhandled promise rejection

I'm trying to test if my Http Request method throw an error, but I always got: Unhandled promise rejection.
This is my test:
it('it should get the first page of all offers with limit 20 without a cache system', (done) => {
const httpRequestConnector = new HttpRequestConnector(apiConfigs);
const queryArgs: any = {
page: 1,
limit: 20,
};
const functionThatThrows = () => {
httpRequestConnector.offersFindAll(queryArgs, 'admin').then((res) => {
res.should.have.property('status');
res.status.should.equal(200);
res.should.have.property('body');
res.body.should.have.property('request');
res.body.request.should.have.property('page');
res.body.request.page.should.equal('1');
res.body.request.should.have.property('limit');
res.body.request.limit.should.equal('20');
res.body.should.have.property('response');
res.body.response.should.have.property('data');
res.body.response.data.should.have.property('data');
Object.keys(res.body.response.data.data).length.should.equal(20);
}).catch((err) => {
throw err;
});
};
expect(functionThatThrows).to.throw();
done();
});
This is my methods (where the "Offeree" will cause an error):
...
private query(url: string, send: any): Promise<any> {
const httpRequestService: HttpRequestInterface = new HttpRequestService();
return httpRequestService.post({
url,
send,
});
}
offersFindAll(args: any, apiDefinition: string): Promise<any> {
(new GuardAgainstNullValues())
.guard(apiDefinition);
const queryArgs = args || {};
const target: string = (apiDefinition === 'admin') ? 'Offeree' : 'Affiliate_Offer';
const method: string = 'findAll';
const apiInfos: any = this.getApiInfos(apiDefinition);
const url: string = apiInfos.api + '?api_key=' + apiInfos.api_key + '&Target=' + target + '&Method=' + method;
if (queryArgs && 'limit' in queryArgs) {
queryArgs.limit = args.limit;
} else {
queryArgs.limit = 1000;
}
return new Promise((fulfill, reject) => {
return this.query(url, queryArgs)
.then((res) => {
if (res.body.response.status === -1) {
throw new RequestStatusException('Cannot get offers');
}
fulfill(res);
})
.catch((err) => {
reject(err);
});
});
}
...
And this is my POST method from my HttpRequestService:
class HttpRequestService implements HttpRequestInterface{
private engine: SuperAgentStatic;
constructor() {
this.engine = request; // Superagent
}
get({ url, query }: { url: string, query?: any }): Promise<any>{
return new Promise((fulfill: any, reject: any) => {
this.engine
.get(url)
.query(query)
.end((err: ResponseError, res: Response) => {
if (err) {
reject(err);
}
fulfill(res);
});
});
}
post({ url, send }: { url: string, send?: any }): Promise<any>{
return new Promise((fulfill: any, reject: any) => {
this.engine
.post(url)
.send(qs.stringify(send))
.end((err: ResponseError, res: Response) => {
if (err) {
reject(err);
}
fulfill(res);
});
});
}
};
I wrapped my offersFindAll request with a promise to handle if (res.body.response.status === -1) { to throw an error instead of throwing it anywhere i use this method.
I'm trying to expect the function to throw here in my mocha test: expect(functionThatThrows).to.throw();
But i always got a (node:7485) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): RequestStatusException: Cannot get offers which is the good exception, but I can't handle the promise correctly.
I try to only return this.query without wrapping it in a promise, but does the same.
How can I structure this correctly to test if it throws?
Thank you very much for your help
In your method offersFindAll():
return new Promise((fulfill, reject) => {
return this.query(url, queryArgs)
.then((res) => {
if (res.body.response.status === -1) {
throw new RequestStatusException('Cannot get offers');
}
fulfill(res);
})
.catch((err) => {
reject(err);
});
});
the exception cannot be caught by the catch clause. It's better to write so:
return new Promise((fulfill, reject) => {
return this.query(url, queryArgs)
.then((res) => {
if (res.body.response.status === -1) {
reject('Cannot get offers');
}
fulfill(res);
})
.catch((err) => {
reject(err);
});
});
Meanwhile, the stacktrace of the errors can be found using:
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at:', p, 'reason:', reason);
// application specific logging, throwing an error, or other logic here
});

Categories