In Node.js, I need to read a file and validate it's contents, all in async. I m using Node.js 6.6, bluebird 3.4.6
Example code:
// pseudo function to read file contents - resolves when 'flag' is true, rejects when 'flag' is false.
function readFile(flag) {
return new Promise(function (resolve, reject) {
console.log('Reading file...');
if (flag) {
resolve('File contents');
} else {
reject('readFile error');
}
});
}
// pseudo function to validate file contents - resolves when 'flag' is true, rejects when 'flag' is false.
function validate(fileContents, flag) {
return new Promise(function (resolve, reject) {
console.log('Validating file: ', fileContents);
if (flag) {
resolve('Validate passed');
} else {
reject('validation failed');
}
});
}
readFile(false)
.then(function (fileContents) {
console.log('Successfully read the file:', fileContents);
return fileContents;
})
.catch(function (fileReadErr) {
console.log('Failed to read the file:', fileReadErr);
throw fileReadErr; // or, return Promise.reject(err);
})
.then(function (fileContents) {
return validate(fileContents, false);
})
.then(function (result) {
console.log('Successfully validated the file:', result);
})
.catch(function (err) {
console.log('Failed to validate the file:', err);
})
;
<script src="https://cdn.jsdelivr.net/bluebird/3.4.6/bluebird.min.js"></script>
The above code will print
Reading file...
Failed to read the file: readFile error
Failed to validate the file: readFile error
The above promise chain roughly translates to below sync code:
try {
let fileContents;
try {
fileContents = readFile(false);
console.log('Successfully read the file:', fileContents);
} catch (e) {
console.log('Failed to read the file:', e);
throw e;
}
let validationResult = validate(fileContents, false);
console.log('Successfully validated the file:', validationResult);
} catch (err) {
console.log('Failed to validate the file:', err);
}
And, throwing or rejecting in the first catch method will still invoke the 2nd catch method.
My question: Is there any way to break the chain once the file reading is failed? My objective is to return different HTTP status codes (file read error: 500, validation failed: 400) from an express.js route.
I know a solution using non-standard specialized catch method, but that requires special handling. In the sense, I need to throw errors or need some filtering key in the error object and both of which are not in my hands, and involves some work to achieve it. This solution is mentioned in bluebird docs & here: Handling multiple catches in promise chain
The simplest solution by far is to use what I call "insulated catches". ie, a pattern in which each .catch() is a specialist, associated with a particular step in the overall process, and the main chain comprises only .thens (and eventually a single, terminal catch).
Also, it is useful in this kind of circumstance to convey added information down the error path by re-throwing Error objects with added properties. This avoids the need for custom Errors.
Promise.resolve()
.then(function() {
return readFile(false)
.then(function (fileContents) {
console.log('Successfully read the file:', fileContents);
return fileContents;
})
.catch(function (error) {
error.code = 521; // or whatever
error.customMessage = 'Failed to read the file';
throw error;
})
})
.then(function (fileContents) {
return validate(fileContents, false)
.then(function (result) {
console.log('Successfully validated the file:', result);
return fileContents;
})
.catch(function (error) {
error.code = 522; // or whatever
error.customMessage = 'Failed to validate the file';
throw error;
});
})
.catch(function(error) { // terminal catch.
console.log(error);
// It's possible for unaugmented errors to reach this point,
// so be sure to test for the extra properties before trying to use them.
if(error.code) {...}
if(error.customMessage) {...}
// Note also that the original error.message is still intact.
});
The initial Promise.resolve() isn't strictly necessary, but helps keep everything else symetrical.
This will work with any Promises/A+ lib. Bluebird-sugar is not required.
You can create custom Error types like so:
ReadFileError = function() {};
ReadFileError.prototype = Error.prototype;
ValidationError = function() {};
ValidationError.prototype = Error.prototype;
Then, you can throw from a Promise instead of rejecting:
function validate(fileContents, flag) {
return new Promise(function (resolve, reject) {
console.log('Validating file: ', fileContents);
if (flag) {
resolve('Validate passed');
} else {
throw new ReadFileError('readFile error');
}
});
}
Then you can catch different errors based on their types:
readFile(false)
.then(function (fileContents) {
console.log('Successfully read the file:', fileContents);
return fileContents;
})
.then(function (fileContents) {
return validate(fileContents, false);
})
.then(function (result) {
console.log('Successfully validated the file:', result);
})
.catch(ReadFileError, function (err) {
console.log(..., err);
})
.catch(ValidationError, function (err) {
console.log(..., err);
})
catch(function(err) {
...
});
Maybe more people could get the same problem. I personally don't think this is the best way to do this, because then you have your app throwing pseudo error, which could mistakenly be processed by other error processing on your server. But it works like you proposed:
// pseudo function to read file contents - resolves when 'flag' is true, rejects when 'flag' is false.
function readFile(flag) {
return new Promise(function (resolve, reject) {
console.log('Reading file...');
if (flag) {
resolve('File contents');
} else {
throw new Error ('errorReading');
}
});
}
// pseudo function to validate file contents - resolves when 'flag' is true, rejects when 'flag' is false.
function validate(fileContents, flag) {
return new Promise(function (resolve, reject) {
console.log('Validating file: ', fileContents);
if (flag) {
resolve('Validate passed');
} else {
throw new Error ('validationFailed');
}
});
}
readFile(false)
.then(function (fileContents) {
console.log('Successfully read the file:', fileContents);
return fileContents;
})
.then(function (fileContents) {
return validate(fileContents, false);
})
.then(function (result) {
console.log('Successfully validated the file:', result);
})
.catch((error) => {
console.log(error.name);
console.log(error.message);
if (error.message === 'errorReading'){
console.log('error 500 - File could\'d be read');
// Maybe more custom error processing here
//return res.status(500).send(JSON.stringify({
// 'status' : 'error',
// 'message' : 'File could\'d be read'
//}));
} else if (error.message=== 'validationFailed'){
console.log('error 500 - Validation not OK');
// Maybe more custom error processing here
//return res.status(500).send(JSON.stringify({
// 'status' : 'error',
// 'message' : 'Validation not OK'
//}));
} else {
console.log('error 500 - Some really bad stuff!');
//return res.status(500).send(JSON.stringify({
// 'status' : 'error',
// 'message' : 'Some really bad stuff!',
// 'errorMessage': error
//}));
}
});
<script src="https://cdn.jsdelivr.net/bluebird/3.4.6/bluebird.min.js"></script>
Please note that I commented out the res.send of express to avoid errors on the processing of this snippet!
As far as i understand what you want to achieve i would suggest always using one single catch block (when can be avoided introducing nesting in promise logic which is totally ok in few use cases, but should be avoided when it can be, because you will potentially ends up with promise hell with indentation all around)
Can you handle all errors in your functions readFile, validate in uniform way like:
const error = new Error('something bad happened')
error.status = 500
return reject(error)
Then you could do you handle error logic within one single catch block based on status such as res.status(err.status || 500).json(...)
Related
I'm refactoring my code to remove a "callback hell" using Promises, but encountered an error that I cannot pass. My code receives list of IDs and processes them making few database calls, that is why I had this "callback hell".
Everything worked fine until Promises. The res is equal 0 when I had to respond back to the client.
function processVMDelete(returnedVMIDs){
return new Promise((resolve, reject) => {
var mariasqlClient = dbConnection();
mariasqlClient.query( sqlUpdateDELETE_STATE_ByVMID, [
'DELETE',
returnedVMIDs
], function(err, rows) {
if (err){
reject(err);
}
console.log('finish update');
// dont' need to return anything here
resolve(0);
});
mariasqlClient.end();
});
}
function getListExpVM(){
return new Promise((resolve, reject) => {
var vmList = [];
var mariasqlClient = dbConnection();
mariasqlClient.query( sqlSearch_ByUSERNAMEAndSTATE, [
requesterUsername,
'ACTIVE'
], function(err, rows) {
if (err){
reject(err);
}
vmList = filterExpiredVMs(rows);
var response = {
status : 200,
success : 'Successfull',
data : vmList,
requester: requesterUsername
};
resolve(response);
});
mariasqlClient.end();
});
}
router.post('/processVMs', function(req, res) {
var returnedVMIDs = JSON.parse(req.body.data);
processVMDelete(returnedVMIDs)
.then(res => {
console.log('done');
// check if there is more available for the user:
getListExpVM()
.then(response => {
console.log('sending back list of VMs');
//===>>> ERROR HERE: res.end is not a function
res.end(JSON.stringify(response));
})
.catch(err => {
console.log('error', err.message);
logger.error("Error getting expired VMs: " + err.message);
//===>>> ERROR HERE: res.send is not a function
res.status(500).send({error: err.message})
});
})
.catch(err => {
console.log('error', err.message);
logger.error("Error processing VMs: " + err.message);
//===>>> ERROR HERE: res.send is not a function
res.status(500).send({error: err.message})
});
});
You've redefined res with this:
processVMDelete(returnedVMIDs)
.then(res => {...})
This will hide the higher scoped res associated with the overall request (the one you need to use for res.end()). Change the name of this one to something else like result and then change the corresponding references that use this result.
I'm using the aws-sdk to upload a message to kinesis. It works fine but i'm trying to solve for when the action does not work (due to internet being down or something).
Here is the code i'm running.
const returnValue = kinesis.putRecord(params, (err, data) => {
if (err) {
logger.info(err);
return err;
} else {
logger.info(data);
return data;
}
});
console.log(returnValue);
I expect to get back the err or data. Instead what I get back is a Huge json of what I assume is the kinesis.PutRecord request itself.
Any ideas on why i'm not getting data returned to my returValue variable?
The console logs data and error just fine.
UPDATE:
as pointed out in the comments, I needed to wrap the function in a promise. I also put it in a try catch since if you throw a "reject" without a catch you get an unhandled promise rejection.
Updated code here:
try {
const returnValue = await new Promise(function(resolve, reject){
kinesis.putRecord(params, (err, data) => {
if (err) {
logger.info(err);
reject(err);
} else {
logger.info(data);
resolve(data);
}
});
});
logger.log(returnValue);
} catch (error) {
logger.error(error);
return error;
}
I am starting to use promise in NodeJS. The requirement is to use these because of async calls. Here is the first code I wrote for promise.
function asyncFunc(data) {
return new Promise(
function(resolve, reject) {
try {
resolve(data);
} catch (err) {
reject("Custom Error");
}
});
}
//Usage:
asyncFunc('Sample String')
.then(result => { console.log(result); })
.catch(error => { console.log(error); });
//Output:
Sample String
null
//If I change the code to:
function asyncFunc(data) {
return new Promise(
function(resolve, reject) {
try {
reject("Custom Error");
} catch (err) {
resolve("Data");
}
});
}
//Output:
//Exception has occurred: string
//I get the above exception at line: reject("Custom Error");
So the question is "reject" can only be used from "catch" block? Why can't I raise "reject" event manually? What is the scenario "reject" is used? Can someone provide me a better example where I can use both "resolve" and "reject"?
You can use reject if you want, of course.
Let's suppose you have a function that resolves a promise if a person is adult, and rejects it if not.
You'd have something like this:
function asyncFunc(age) {
return new Promise((resolve, reject) => {
if(age >= 18) {
resolve(true);
} else {
// here you call reject manually outside of a catch block
reject(false);
// or
// reject(Error('not adult'));
}
});
}
usage:
asyncFunc(19).then(result => {
console.log(result); // true
})
.catch(error => {
console.log(error); // false or Error('not adult')
});
Probably you got something like (node:5009) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Looks like you run your asyncFunc(..) in console. In this case node.js first execute your function after your hit Enter. Try to take your code to curly brackets like this:
{
asyncFunc('Sample String')
.then(result => { console.log(result); })
.catch(error => { console.log(error); });
}
In case of a try/catch block you usually reject at catch but you can also reject in try.
resolve and reject are basically callbacks for Promise. if you resolve it will got to the next chain if you reject it will break the chain.
So should use reject when an error occurs cause reject will break the promise chain.
For example.
Lets say you have a function that works with callback and you want to wrap it in a Promise like function. This function will check if user exists in database. If the User is found it will return true if not false and if there is an error in database (eg connection) it will reject.
function checkUserExist(id) {
return new Promise((resolve, reject) => {
checkUserExistInDatabase(id, function(result, error) {
if (error) {
reject(error);
}
if (result != null) {
resolve(true);
} else {
resolve(false);
}
})
});
}
function databaseError() {
return new Promise((resolve, reject) => {
reject();
})
}
var idsThatExist = [];
checkUserExist(1).then(function(exist) {
if (exist)
idsThatExist.push(1);
return checkUserExist(2)
}).then(function(exist) {
if (exist)
idsThatExist.push(2);
return databaseError(3)
}).then(function(exist) {
//WILL never Reach here
if (exist)
idsThatExist.push(3);
return checkUserExist(4)
}).then(function(exist) {
if (exist)
idsThatExist.push(4);
}).catch(function(err) {
//it will skip checkUserExist(4)
console.log('I got rejected after checked users:'
idsThatExist)
})
So the question is "reject" can only be used from "catch" block?
No, You can use reject anywhere. Catch isn't necessary to use reject
Why can't I raise "reject" event manually?
You can reject using creating new Promise() or static methods of Promise . See promiseUsingNew() and promiseUsingStaticMethod()
What is the scenario "reject" is used?
try/catch is used for error handling in synchronous programming. resolve & reject is for error handling in asynchronous programming operation instead of callbacks.
Can someone provide me a better example where I can use both "resolve" and "reject"?
'use strict';
function promiseUsingNew(marks) {
return new Promise((resolve, reject) => {
if (marks < 0 || marks > 100) {
return reject('Invalid marks');
}
if (marks >= 40) {
return resolve('You passed');
} else {
return resolve('You Failed');
}
});
}
function promiseUsingStaticMethod(marks) {
if (marks < 0 || marks > 100) {
return Promise.reject('Invalid marks');
}
if (marks >= 40) {
return Promise.resolve('You passed');
} else {
return Promise.resolve('You Failed');
}
}
// you can use promiseUsingNew(marks) or promiseUsingStaticMethod(marks)
promiseUsingNew(221).then((result) => {
console.log(result);
}).catch((error) => {
console.log(error);
});
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()
})
I have a nodeJS application using the mssql driver to interact with my SQL database. I want to have a single function to get a value from a database, however, on first use, the table won't exist, so if there is a specific error, I want to call my createValue() function. The code below works, but I have to call it twice to get the value. Basically, if the condition in the .catch is met, I would like to call the Request again. Is there a neat way of doing this?
var value;
new sql.Request().query('select * from _table')
.then(function (recordset) {
value = recordset;
})
.catch(function (err) {
console.log("Query Error: " + err);
if (err.message == "Invalid object name '_table") {
createValue();
}
})
Update:
I now have the following function, but how should I best get it to return recordset?
function getData() {
sql.connect("mssql://username:password#localhost/mytestdatabase").then(function () {
return new sql.Request().query('select * from _table')
.then(function (recordset) {
console.log(recordset); // <-- THIS IS WHAT I WANT TO RETURN
})
.catch(function (err) {
console.log("Query Error: " + err);
if (err.message == "Invalid object name '_table") {
updateValue();
return getData();
}
return null;
})
}).catch(function (err) {
console.log("Connection Error: " + err);
})
};
You can wrap it in a function, which you can then call recursively.
I'm assuming createValue() is synchronous.
function getData(){
return new sql.Request().query('select * from _table')
.catch(function (err) {
if (err.message == "Invalid object name '_table") {
createValue();
return getData();
}
throw err;
});
}
...
var resultsetPromise = getData();
resultsetPromise.then( function(resultset){
// do something with your data
}).catch( function(err){
console.log("Query Error: " + err);
});