Unhandled promise rejection in Node.js - javascript

I'm trying to make a DELETE call and I'm implementing the function below. I understand that in a promise, there needs to be a "resolve" and a "reject" state, but I'm getting an unhandled promise rejection error:
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): [object Object]
I don't really like using conditional statements inside a promise because it gets messy, but what I'm trying to do here is to check if the organization is verified, and if it is, a delete operation should not occur and will reject.
function deleteOrg(id) {
return new Promise((resolve, reject) => {
// A helper function that returns an 'org' object
findById(id)
.then((orgObject) => {
if (orgObject.verified_at !== null) {
throw new Error(422, 'Unable to delete organization')
}
//Organization is not verified, so proceed to delete
new Organization().where({'id': id}).destroy()
.then(() => {
return resolve() //return 200 upon deletion
})
.catch((err) => {
return reject(new Error(500, 'Unable to delete organization'))
})
})
.catch((err) => {
const m = `Unable to delete organization: ${err.message}`
return reject(new Error(500, m))
})
})
}
I'm pretty sure I'm handling the rejection inside the if wrong.

as findById and .destroy return Promises, there is no need for the Promsie constructor
Your code is then simplified to
function deleteOrg(id) {
return findById(id)
.then((orgObject) => {
if (orgObject.verified_at !== null) {
throw new Error(422, 'Unable to delete organization')
}
//Organization is not verified, so proceed to delete
return new Organization().where({'id': id}).destroy()
.catch((err) => {
throw (new Error(500, `Unable to delete organization: ${err.message}`));
});
});
}

Creating promises inside promise constructors is a known anti-pattern. Try modularizing your promises into separate functions instead:
function deleteOrg(id) {
const verifyOrg = (orgObject) => {
if (orgObject.verified_at !== null) {
throw new Error(422, 'Unable to delete organization')
}
};
const destroyOrg = () => new Organization().where({
'id': id
}).destroy();
return findById(id)
.then(verifyOrg)
.then(destroyOrg);
}
Where you can let the errors propagate through the promise chain and handle them outside:
deleteOrg(id)
.catch((err) => {
const m = `Unable to delete organization: ${err.message}`;
// ...
});

original method() => {
try{ //code to raise the exceptio
})
;
The best way to handle is to use expect It can be matched with an exception. A sample test
someMock.mockFunc(() => {
throw new Error("Something");
});
test('MockFunc in error', () => {
return expect(orginalCall()).rejects.toMatch('Something');
});

Related

How do I fix a race condition in a nested Promise in Node.js? [duplicate]

I'm creating an API using Node.js/TypeScript running Express. Below is an excerpt from my get method. An error is being triggered in the format method, which throws an error that is caught by the promise, but not propagated to the parent promise after a throw:
this.getModel(objectName).findAll(queryParameters).then(function(databaseObjects) {
for (let databaseObject of databaseObjects) {
var jsonObject = {};
//console.log("Database object: ");
//console.log(databaseObject);
transform.baseFormat(databaseObject, jsonObject)
.then(() => transform.format(databaseObject, jsonObject))
.then(() => {
res.locals.retval.addData(jsonObject);
}).catch((e) => {
console.log("Caught error during format of existing object: ");
console.log(e);
throw e;
});
}
})
.then(() => {
if (metadata) {
this.metadata(objectName, false, transform, res.locals.retval);
delete queryParameters.limit;
delete queryParameters.offset;
console.log("RUNNING METADATA COUNT: ");
this.getModel(objectName).count(queryParameters).then(function(count) {
res.locals.retval.setMetadata("records", count);
return next();
}).catch(function(e) {
this.error(e, res);
return next();
});
} else {
console.log("NO METADATA");
return next();
}
})
.catch((e) => {
// TODO: Move status into error() function
console.log("500 Error on GET");
console.error(e);
res.locals.retval.addError(ErrorCode.InternalError, e);
res.status(ErrorCode.InternalError).send(res.locals.retval);
return next();
});
Here's the output:
(node:8277) Warning: a promise was created in a handler at /Library/WebServer/adstudio/dist/server.js:555:51 but was not returned from it, see
at Function.Promise.bind (/Library/WebServer/adstudio/node_modules/bluebird/js/release/bind.js:65:20)
Caught error during format of existing object:
Test Error
END FUNCTION HAS BEEN REACHED!
Then the request fails to finish.
I've read a lot on Promises and I haven't been able to find an issue/solution similar to mine.
http://bluebirdjs.com/docs/warning-explanations.html
http://taoofcode.net/promise-anti-patterns/
https://www.reddit.com/r/javascript/comments/4bj6sm/am_i_wrong_to_be_annoyed_with_promise_error/
https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
Chained promises not passing on rejection
http://wiki.commonjs.org/wiki/Promises/A
https://promisesaplus.com/
Running inside that for-loop is not asynchronous, so your promise is resolving basically as soon as the loop finishes, yet before all your formatting finishes.
Use a promise control flow, like bluebird's Promise.each which is serial or just Promise.all. Then any exceptions will be caught.
this.getModel(objectName).findAll(queryParameters).then(function (databaseObjects) {
var promises = databaseObjects.map(databaseObject => {
var jsonObject = {}
// console.log("Database object: ");
// console.log(databaseObject);
return transform.baseFormat(databaseObject, jsonObject)
.then(() => transform.format(databaseObject, jsonObject))
.then(() => {
res.locals.retval.addData(jsonObject)
}).catch((e) => {
console.log('Caught error during format of existing object: ')
console.log(e)
throw e
})
})
return Promise.all(promises)
})
.catch((e) => {
// TODO: Move status into error() function
console.log('500 Error on GET')
console.error(e)
res.locals.retval.addError(ErrorCode.InternalError, e)
res.status(ErrorCode.InternalError).send(res.locals.retval)
return next()
})

How to properly use resolve and reject for promises

I've started to look at using Promises and have begun by putting together a simple function and calling it a few times. I need a sanity check around reject and resolve.
Is this the correct way to "promisify" a function?
Is this the correct way to deal with reject and resolve?
Anything I've got totally wrong?
const Redis = require('ioredis');
const redis = new Redis({
port: 6379,
host: '127.0.0.1'
});
function checkValues(name, section) {
return new Promise((resolve, reject) => {
redis.multi()
.sismember('names', name)
.sismember('sections', section)
.exec()
.then((results) => {
if(results[0][1] === 1 && results [1][1] ===1) {
reject('Match on both.');
} else if(results[0][1] === 1 || results [1][1] ===1) {
reject('Match on one.');
} else {
redis.multi()
.sadd('names', name)
.sadd('sections', section)
.exec()
.then((results) => {
// Lazy assumption of success.
resolve('Added as no matches.');
})
// No catch needed as this would be thrown up and caught?
}
})
.catch((error) => {
console.log(error);
});
});
}
// Call stuff.
checkValues('barry', 'green')
.then((result) => {
// Added as no matches "resolve" message from 'barry', 'green'
console.log(result);
retutn checkValues('steve', 'blue');
})
.then((result) => {
// Added as no matches "resolve" message from 'steve', 'blue'
retutn checkValues('steve', 'blue');
})
.then((result) => {
// Match on both "reject" message from 'steve', 'blue'
console.log(result);
})
.catch((error) => {
console.log(error);
});
No, this is kind of an anti-pattern. You already have a function that returns a promise so you don't need to wrap it in another promise, you can just return it. Remember that then() returns a promise that resolves to the return value of then. You can also return another promise from then. Usually this looks super clean, but in this case you need some logic in the then function, so it gets a little messy.
function checkValues(name, section) {
// Just return this Promise
return redis.multi()
.sismember('names', name)
.sismember('sections', section)
.exec()
.then((results) => {
if(results[0][1] === 1 && results [1][1] ===1) {
// Rejections will be caught down the line
return Promise.reject('Match on both.');
} else if(results[0][1] === 1 || results [1][1] ===1) {
return Promise.reject('Match on one.');
} else {
// You can return another Promise from then()
return redis.multi()
.sadd('names', name)
.sadd('sections', section)
.exec()
}
})
// You don't need to catch here - you can catch everything at the end of the chain
}
Several points:
Don't use the explicit-promise-construction-antipattern
As a general guide in purging the anti-pattern, after removing the new Promise() wrapper, change resolve statements to return and reject statements to throw new Error(...).
.catch() catches! If errors are to be observable/handleable by the caller, then either don't catch in checkValues() or catch and re-throw. Catching without re-throwing will cause the returned promise to settle on its success path, never its error path, which is great for error recovery but not always appropriate.
Suggest that all three cases, 'Match on both', 'Match on one' and 'Added as no matches', are really successes. Unless there's a particular reason for wanting 'Match on both' and 'Match on one' to be seen as error conditions, then return rather than reject/throw. That way, your call stuff chain will progress down its success path, .then().then().then(), regardless of expected outcome; only an unexpected error will go down the error path to be caught by the final .catch(). This isn't a general rule; very often, throwing is the right thing to do, but not here.
function checkValues(name, section) {
return redis.multi()
.sismember('names', name)
.sismember('sections', section)
.exec()
.then((results) => {
if(results[0][1] === 1 && results [1][1] === 1) {
return 'Match on both.';
} else if(results[0][1] === 1 || results [1][1] ===1) {
return 'Match on one.';
} else {
return redis.multi()
.sadd('names', name)
.sadd('sections', section)
.exec()
.then((results) => {
return 'Added as no matches.';
});
}
})
.catch((error) => {
console.log(error);
throw error;
});
}
// Call stuff.
checkValues('barry', 'green')
.then((result) => {
console.log(result); // expect 'Added as no matches'
return checkValues('steve', 'blue');
})
.then((result) => {
return checkValues('steve', 'blue'); // expect 'Added as no matches'
})
.then((result) => {
console.log(result); // expect 'Match on both'
})
.catch((error) => {
// only an unexpected error will land here
console.log(error);
});

Propagate rejected promise via a .catch()

I have some code which essentially looks like this:
export function firstFunction(req: express.Request, res: express.Response, next: express.NextFunction): void {
secondFunction(id)
.then((userId: UserId) => {
res.status(200).send(UserId);
})
.catch((err) => {
if (err instanceof NoResultError) {
res.status(404).send(err);
} else {
next(err);
}
});
}
export function secondFunction(id: string): Promise<UserId> {
return new Promise<UserId>((resolve, reject) => {
thirdFunction(id)
.then((data: TableInfo) => {
if (Object.keys(data).length !== 3) {
reject(new Error('data in database is not mapped properly'));
}
resolve(data);
})
.catch((err) => {
// WANT TO PROPAGATE ERROR UP TO THE GETDETAILS FUNCTION WHICH CALLS THIS
});
});
}
export function thirdFunction(id: string): Promise<TableInfo> {
return new Promise<TableInfo>((resolve, reject) => {
let query = `
//query goes here
`;
db.executeQuery(query, [id])
.then((data: TableInfo) => {
if (Object.keys(data).length < 1) {
reject(new NoResultError('some message here'));
}
resolve(data);
});
});
}
My goal is to have the lowest level of the three functions (thirdFunction) determine if the data from the db-query finds no data and then reject that promise with an error. Then the secondFunction should ideally catch this error and propagate it up to firstFunction so that firstFunction can handle that error properly. I have tried doing a throw err a return err and a return Promise.reject(err) all of which lead to an unhandled promise rejection. What is my (probably fundamental) misunderstanding of how this should work?
the secondFunction should ideally catch this error and propagate it up
No, propagation is the default. Ideally you should not need to catch it at all, and it will propagate up automatically.
I have tried things that all lead to an unhandled promise rejection. What is my (probably fundamental) misunderstanding?
You're using the Promise constructor antipattern! With it, you would need to call reject(err) in the catch handler to make it work. Or really .then(resolve, reject);. But that's absolutely not how this should be done.
Instead, drop the new Promise wrapper and just return the result of chaining then handlers:
export function secondFunction(id: string): Promise<UserId> {
return thirdFunction(id)
.then((data: TableInfo) => {
if (Object.keys(data).length !== 3) {
throw new Error('data in database is not mapped properly');
}
return getUserId(data);
});
}
export function thirdFunction(id: string): Promise<TableInfo> {
let query = `/* query goes here */`;
return db.executeQuery(query, [id])
.then((data: TableInfo) => {
if (Object.keys(data).length < 1) {
throw new NoResultError('some message here');
}
return data;
});
}

TypeScript error TS2345 when rejecting a Promise with an error

I have a TypeScript error message whose error I do not understand. The error message is:
error TS2345: Argument of type '(error: Error) => void | Promise' is not assignable to parameter of type '(reason: any) => IdentityKeyPair | PromiseLike'.
Type 'void | Promise' is not assignable to type 'IdentityKeyPair | PromiseLike'.
My code was working fine but TypeScript got mad at me when I changed this block:
.catch((error) => {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
})
into this:
.catch((error) => {
if (error instanceof RecordNotFoundError) {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
} else {
return reject(error);
}
})
Here is the complete code which was working:
public init(): Promise<Array<Proteus.keys.PreKey>> {
return new Promise((resolve, reject) => {
this.store.load_identity()
.catch((error) => {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
})
.then((identity: Proteus.keys.IdentityKeyPair) => {
this.identity = identity;
return this.store.load_prekey(Proteus.keys.PreKey.MAX_PREKEY_ID);
})
.then((lastResortPreKey: Proteus.keys.PreKey) => {
return resolve(lastResortPreKey);
})
.catch(reject);
});
}
And here is the code which does not compile anymore:
public init(): Promise<Array<Proteus.keys.PreKey>> {
return new Promise((resolve, reject) => {
this.store.load_identity()
.catch((error) => {
if (error instanceof RecordNotFoundError) {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
} else {
return reject(error);
}
})
.then((identity: Proteus.keys.IdentityKeyPair) => {
this.identity = identity;
return this.store.load_prekey(Proteus.keys.PreKey.MAX_PREKEY_ID);
})
.then((lastResortPreKey: Proteus.keys.PreKey) => {
return resolve(lastResortPreKey);
})
.catch(reject);
});
}
Does anyone sees why the TypeScript compiler refuses my return reject(error); statement with error code TS2345?
Screenshot:
I am using TypeScript 2.1.4.
Try out below. When you are in a then or catch block you can return a Promise or a value which gets wrapped into a Promise. You are manually working with a Promise yourself so you can just call the resolve and reject handlers without needing to return anything. Returning reject(error) would try to take that returned value, wrap it in a Promise and then try to pass to the next then block which is why you were getting the error you did. Think of it this way: returning something in a handler means continue down the chain with this new value. In your case I think you just want to stop the chaining and have the Promise you are creating resolve or reject under certain conditions.
public init(): Promise<Array<Proteus.keys.PreKey>> {
return new Promise((resolve, reject) => {
this.store.load_identity()
.catch((error) => {
if (error instanceof RecordNotFoundError) {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
} else {
throw error;
}
})
.then((identity: Proteus.keys.IdentityKeyPair) => {
this.identity = identity;
return this.store.load_prekey(Proteus.keys.PreKey.MAX_PREKEY_ID);
})
.then((lastResortPreKey: Proteus.keys.PreKey) => {
resolve(lastResortPreKey);
})
.catch((error) => {
reject(error);
});
});
}
You can't stop a Promise chain (cancellation aside), not even by returnning reject(), which is a definite misuse of Promises (you're not supposed to wrap a Promise in another Promise constructor).
Let's start with what you could do, then go to what you should do.
You could let the rejection bubble down the Promise chain, rethrowing it when it doesn't match your type guard, and at the bottom of the line, after all the .catch() clauses exhausted themselves, the Promise returned from your function will reject.
Now
Think about how you would do it in sync code. You'd have something like this:
try {
try {
actionThatThrows();
} catch (err) {
breakEverything();
}
continue other steps
} catch(err) {
generalErrorHandling();
}
That kind of code is not OK, and it isn't OK in Promises either. You should move distinct actions into functions which can resolve or reject on their own, use Errors as they were meant, an exception that bubbles up the stack until it meets something that can handle it.
Also, and because you're using TS 2.1.x, for long async flows, an async function is recommended.
Your return is useless there, it's the end of the onRejection callback.
And the return reject() will fulfill the next .then() promise anyway.
However, if you throw the error, it'll be inherited in the following promises down to the .catch(reject);
Basically: in any catch/then, return will resolve the child promise, and throw will reject the child promise.
I rewrote your code for a better flow of the promise chain.
public init(): Promise<Array<Proteus.keys.PreKey>> {
return new Promise((resolve, reject) => {
this.store.load_identity()
.catch(
(error) => {
if (error instanceof RecordNotFoundError) {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
} else {
throw error;
}
}
)
.then(
(identity: Proteus.keys.IdentityKeyPair) => {
this.identity = identity;
resolve(this.store.load_prekey(Proteus.keys.PreKey.MAX_PREKEY_ID));
},
reject
)
});
}

Returning one object that's built with nested promises

I'm struggling to wrap my head around a nested promise layout where one one object is returned at the end of it. My current code is as follows:
router
router.get(`/${config.version}/event/:id?`, function (req, res, next) {
var event = new Event(req, res, next);
event.getInfo(req.params.id).then((info) => {
res.send(info);
});
});
function
getInfo(id) {
db.main('events').where('id', id).select()
.then((result) => {
if(result.length > 0) {
var event = result[0];
//regular functions
event.status = this.getStatus(id);
event.content = this.getContent(id);
event.price = this.getPrice(id);
//promise functions
var users = this.getUsers(id);
var hosts = this.getHosts(id);
Promise.all([users, hosts]).then(values => {
event.users = values[0];
event.hosts = values[1];
//return whole event object to router
return event;
})
.catch((err) => {
return {
result: 'error',
error: err
};
});
} else {
return {
result: 'error',
error: "Event does not exist"
};
}
}).catch((e) => {
return {
result: 'error',
error: "Could not retrieve event info"
};
});
}
As you can see, the router initiates a call to get info about an event. The function then does a database call and gets some event data. Thereafter I need to get the users and hosts of the event from a different table, append that info to the event object as well and then return the whole object to the router to be sent to the client.
When I do this I get an error because I'm not returning a promise from the getInfo function, but I'm not sure how or which promise I'm supposed to return.
I'd appreciate some help with this. Thanks
using .then means that you are returning a promise.
function getInfo(id) {
return new Promise(function(resolve, reject) {
resolve('yay!');
})
}
getInfo().then(function(result) { //result = yay! });
to make your code work, simply replace all the returns with resolves, the errors with rejects, and wrap the whole thing with a return new Promise as i did.
getInfo(id) {
return new Promise(function(resolve, reject) {
db.main('events').where('id', id).select()
.then((result) => {
if (result.length > 0) {
var event = result[0];
//regular functions
event.status = this.getStatus(id);
event.content = this.getContent(id);
event.price = this.getPrice(id);
//promise functions
var users = this.getUsers(id);
var hosts = this.getHosts(id);
Promise.all([users, hosts]).then(values => {
event.users = values[0];
event.hosts = values[1];
//return whole event object to router
resolve(event);
})
.catch((err) => {
reject({
result: 'error',
error: err
});
});
} else {
reject({
result: 'error',
error: "Event does not exist"
});
}
}).catch((e) => {
reject({
result: 'error',
error: "Could not retrieve event info"
});
});
});
}
Just wrap your async code in Promise like this:
getInfo(id) {
return new Promise(function(resolve, reject) {
db.main('events').where('id', id).select()
.then((result) => {
//...
resolve(/* result */)
// OR
reject(/* Error */)
})
}
Note: Use resolve and reject instead return
It's a combination of several things, but the main one is that you are never returning anything from getInfo, so your router handler is calling .then on undefined.
Do not call .catch (without throwing inside it) on Promises you intend to return for a caller to consume. This makes it not possible to use .catch, because you recovered the Promise chain into a resolved one.
Whatever you return inside a .then will be merged into the promise chain, so it's not actually a "Promise that resolves with a Promise". Your whole code could be replaced with:
getInfo (id) {
return db.main('events').where('id', id).select()
.then(result => {
if (result.length == 0) {
// you can also just throw your error object thing,
// but standard Error are generally the convention
throw new Error('Event does not exist')
}
const [event] = result
event.status = this.getStatus(id)
event.content = this.getContent(id)
event.price = this.getPrice(id)
return Promise.all([this.getUsers(id), this.getHosts(id)])
.then(([users, hosts]) => {
event.users = users
event.hosts = hosts
// this is the only value that
// this.getInfo(id).then(value => {/* ... */}) will see
return event
}
})
}

Categories

Resources