How do you prevent nested queries/catches in sequelize? - javascript

I think I'm preventing nested queries as much as possible, but I'm honestly not sure. I understand the calls here can all be executed in a single select query, but I did this to simplify the example.
// This example is in TypeScript
// find user
User.find({where:{username:'user'}})
// if found user
.then(function(user) {
return User.find({where:{username:'other_user'}})
// if found other_user
.then(function(other_user) {
// do stuff
return whatever_i_need
}
// if something went wrong, go straight to parent catch
.catch(function(err) {
// do stuff
throw new Error()
}
}
// if previous .then() returned success
.then(function(data) {
return User.find({where:{username:'yet_another_user'}})
// if found yet_another_user
.then(function(yet_another_user) {
// do stuff
return whatever_i_need_again
}
// if something went wrong, go straight to parent catch
.catch(function(err) {
// do stuff
throw new Error()
}
}
// if anything threw an error at any point in time
.catch(function(err) {
// handle the error
}
However, this results in nested promises, which is exactly what promises are meant to prevent. Is this the "max depth" recommended for promises, or am I missing something? Is there a better way to chain queries?

Return the nested promise instead of handling it in the inner blocks to flatten the structure.
User.find({where:{username:'user'}})
.then(function(user) {
if (user) { // if found user
// do stuff
return User.find({where:{username:'other_user'}});
}
throw new Error('user not-found');
})
.then(function(other_user) {
if (other_user) { // if found other_user
// do stuff
return whatever_i_need;
}
throw new Error('other_user not-found');
})
.then(function(data) {
return User.find({where:{username:'yet_another_user'}})
})
.then(function(yet_another_user) {
if (yet_another_user) { // if found yet_another_user
// do stuff
return whatever_i_need_again;
}
throw new Error('yet_another_user not-found');
}
.then(function(data){
// do stuff
})
.catch(function(err) { // if anything threw an error at any point in time
// handle the error
}
Note that a resolved promise means a query is successfully done. That's it all about. A successful query does't guarantee results to be returned. Empty result is a valid outcome of resolved promises.
Note also that the return value from a resolve or reject callback will be wrapped with a resolved promise, and then passed to the next then block, making a meaningful promise chain. Thanks for #Matt's follow-up feedback below regarding this point.

Two points:
Drop .catch(function(err) { throw new Error() }. It does nothing but remove the error message.
You can unnest the inner then calls
So it just should be
User.find({where:{username:'user'}})
.then(function(user) {
return User.find({where:{username:'other_user'}})
})
.then(function(other_user) {
// do stuff
return whatever_i_need
})
// if previous .then() returned success
.then(function(data) {
return User.find({where:{username:'yet_another_user'}})
})
// if found yet_another_user
.then(function(yet_another_user) {
// do stuff
return whatever_i_need_again
})
// if anything threw an error at any point in time
.catch(function(err) {
// handle the error
})

Related

Using each promise value of Promise.map as input of the next loop

I've done whole-day research on how I can get result of each promise in a Promise.map and use it as input in the next iteration during loop of that same Promise.map. I strictly need to do this approach because am using this logic in a database operation that must be ATOMIC and in case of any promise rejection all the previous transactions have to be rolled back.
NOTE: I have used promise.each and it works well only that it does not allow me to associate the individual promises and roll back all if one fails. So Promise.map seem to be the best solution when each promise is carefully resolved and value returned without causing Error: Transaction query already complete in the next loop. Here is the logic with knex:
var obj={};
knex.transaction(function(trx) {
return Promise.map(array, function(item) {
return trx.insert(item).into('table')
.then(returnedFields => {
//do some validation/operation and return the result or the returnedFields themselves as input in the next loop.
//[START EDIT: this responds to comment by # Mikael Lepistö for clarity]
//update obj here to be used in next loop
//[END EDIT]
});
}, {concurrency: 1});
})
.then(function(inserts) {
console.log(inserts.length + 'Items saved.');
})
.catch(function(error) {
console.error(error);
})
Check this out:
var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
});
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
You can check out async.js library https://caolan.github.io/async/docs.html
Here's what you can do
knex.transaction(function(trx) {
return async.mapValuesSeries(array, (item) => {
return trx.insert(item).into('table')
.then(returnedFields => {
//do some validation/operation and return the result or the returnedFields themselves as input in the next loop.
});
}, (error, inserts) => {
if (err) {
console.error(error);
}
console.log(inserts.length + 'Items saved.');
});
});
Thank you everyone who posted great/greater insights. However it has turned out that Promise.each works well with knex and I was making a mistake of calling commit inside then of some individual promises during the loop hence causing Error: Transaction query already complete in next transaction attempt of the subsequent loop. Reason: There was no need calling commit withing the knex transaction context/block since it's automatically triggered in that context.
To note in the answer below:
Using Knex with Promise.each requires you to listen to possible rejection inside thenblock of each promise and with a try/catch and in some cases explicitly reject else the subsequent promises/values will continue looping and that cannot make database atomic!!!
knex.transaction(function(trx) {
return Promise.map(array, function(item) {
return trx.insert(item).into('table')
.then(returnedFields => {
try{
//do some validation/operation and return the result or the returnedFields themselves as input in the next loop.
/* START Testing a case of rejection */
if (array.indexOf(item) === 3) {
//uncomment the code below to test
//throw new Error('BreakException');
}
/* END */
}catch(err){
fail=true;
}
}).then(val => {
if (fail) {
trx.rollback()//you can ignore this as is automatically triggered here - tested
return Promise.reject(new Error('Rejected'))
}
return val
})
.catch(err => {
fail = true
trx.rollback()//you can ignore this as is automatically triggered here - tested
return Promise.reject(new Error('Rejected'))
});
});
})
.then(function(inserts) {
console.log(inserts.length + 'Items saved.');
})
.catch(function(error) {
console.error(error);
})

Bluebird Promise.any() early reject?

I'm using promise library Bluebird in all my Node.js projects. For getting the content of the first existent file from a list of file paths I use Promise.any successfully as follows:
Promise.any([
'./foo/file1.yaml',
'./foo/file2.yaml',
'./foo/file3.yaml'
], function(filePath) {
_readFile(filePath);
}),then(function(fileContent) {
_console.log(fileContent);
});
My question is, how can i leave the Promis.any loop early if i get an error which is different from "file not found", when reading a file? The following code illustrates my question:
Promise.any([
'./foo/file1.yaml',
'./foo/file2.yaml',
'./foo/file3.yaml'
], function(filePath) {
_readFile(filePath)
.catch(function(err) {
var res = err;
if (err.code == FILE_NOT_FOUND) {
// continue the Promise.any loop
} else {
// leave the Promise.any loop with this error
err = new Promise.EarlyBreak(err);
}
Promise.reject(err);
});
}).then(function(fileContent) {
_console.log(fileContent);
}, function(err) {
// the first error different from FILE_NOT_FOUND
});
May be Promise.any is not the right function?
Leaving a Promise.any() loop early is conceptually problematic in that Promise.any() is an aggregator not a loop, and accepts an array of promises, each of which has a life of its own, not determined by Promise.any().
However, starting with an array of paths, the loop you seek can be expressed as a paths.reduce(...) expression, which builds a .catch() chain, straightforwardly as follows :
function getFirstGoodFileContent(paths) {
paths.reduce(function(promise, path) {
return promise.catch(function() {
return _readFile(path);
});
}, Promise.reject()); // seed the chain with a rejected promise.
}
Catch chain: credit Bergi
The .catch chain thus built, will progress to the next iteration on failure, or skip to the end of the chain on success. This flow control is the inverse of what happens in a more normal .then chain (seeded with a fulfilled promise).
But that's not quite everything. An extra condition is required - namely to "leave the [Promise.any] loop early if I get an error which is different from 'file not found'". This is very simply engineered into the catch chain by sending all errors except FILE_NOT_FOUND down the success path, thereby :
effecting the required flow control (skipping the rest of the chain), but
ending up with an error condition going down the success route - undesirable but recoverable.
function getFirstGoodFileContent(paths) {
paths.reduce(function(promise, path) {
return promise.catch(function() {
return _readFile(path).catch(function(err) {
if (err.code == FILE_NOT_FOUND) {
throw err; // Rethrow the error to continue down the catch chain, seeking a good path.
} else {
return { isError: true, message: err.code }; // Skip the rest of the catch chain by returning a "surrogate success object".
}
});
});
}, Promise.reject()).then(function(fileContent) {
// You will arrive here either because :
// * a good path was found, or
// * a non-FILE_NOT_FOUND error was encountered.
// The error condition is detectable by testing `fileContent.isError`
if (fileContent.isError) {
throw new Error(fileContent.message); // convert surrogate success to failure.
} else {
return fileContent; // Yay, genuine success.
}
});
}
So you can now call :
getFirstGoodFileContent([
'./foo/file1.yaml',
'./foo/file2.yaml',
'./foo/file3.yaml'
]).then(function(fileContent) {
_console.log(fileContent);
}, function(error) {
// error will be due to :
// * a non-FILE_NOT_FOUND error having occurred, or
// * the final path having resulted in an error.
console.log(error);
});

Chain multiple promises in Angularjs

I have the following structure and need to simplify it,
Resuable service1 to interact with IndexedDB
function isDbExist (){
// do something , it return either reject or resolve
}
function createDB (){
// do something , it return either reject or resolve
}
function getData (){
// do something , it return either reject or resolve
}
In another service2 I'm injecting this service1 and using the functions as such
function1 (){
service1.isDbExist.then(function(data){
service1.createDB.then(function(data){
service1.getData.then(function(data){
referred.resolve(data);
},function(error){
deferred.reject(error)
})
},function(error){
deferred.reject(error);
})
},function(error){
deferred.reject(error);
})
}
The problem here is the readability of the code is not good, its not easy to debug as which reject function is for which promise. Is their some good way of doing this ? I have read about $q.all but don't this its applicable in this situation.
Exactly, the beauty of promises is that you can chain them as opposed to nesting them like callbacks. You can return another promise when dealing with the previous like so:
isDbExists().then(function(db) {
// do something
return createDb();
}).then(function(db) {
// do something with with the resolved db..
return getData();
}).then(function(data) {
// Do something with data
}).catch(function(error) {
// Uh oh, something went wrong. Deal with error
});
Finally, you deal with the error that may have occurred.
You can chain multiple promises like this:
service1.isDbExist()
.then(service1.createDB)
.then(service1.getData)
.then(function() {
// DB exists, created and data loaded
})
.catch(function(err) {
console.log('Handle error', err); // isDbExist, createDB or getData rejected
});
Check interactive demo to see how success and error data is passed to the next promise in chain and how you have full control over process on each step:
Demo: http://plnkr.co/edit/PM06e8NGJvHKmoJ9C2Lf?p=info
One of the really great things about promises is that they can be chained like this:
function1 (){
return service1.isDbExist.then(function(exists){
if(!exists) return service1.createDB()
})
.then(function() {
return service1.getData('some', 'args')
});
}
Remember that the .then will receive the result of the previous function, like the exists would be a boolean. function1 will also return a promise and this promise will return the resolved/rejected promise from service1.getData.

Promisifying an synchorous function

So, I have a code which is something like this.
getSomethingAsync(something)
.then(doSomethingAsync)
.then(function(d) {
_d = doSomethingSync(d);
return doSomethingAsyncNext(_d);
})
.then(function(val) {
//All done
})
.catch(err_handler);
I want to make it into something like.
getSomethingAsync(something)
.then(doSomethingAsync)
.then(doSomethingSync)
.then(doSomethingAsyncNext)
.then(function(val) {
//All done
})
.catch(err_handler);
Should I just change doSomethingSync which is:
function(data) {
// do a lot of things with data, throw errors for invalid data
return changed_data;
}
to:
function(data) {
// do a lot of things with data, throw errors for invalid data
return new Promise(function(resolve,reject){
resolve(changed_data);
});
}
or:
function(data) {
return new Promise(function(resolve,reject){
// do a lot of things with data, reject for invalid data
resolve(changed_data);
});
}
Should I just change doSomethingSync which is ...
You don't have to change it all. If the return value of the callback is not a promise, it is directly used to resolve the promise returned by .then. The .then callback does not have to return a promise.
You could write
return Promise.resolve(changed_data);
but again, there is no need. return changed_data; will work just as well.

Handling multiple catches in promise chain

I am still fairly new to promises and am using bluebird currently, however I have a scenario where I am not quite sure how to best deal with it.
So for example I have a promise chain within an express app like so:
repository.Query(getAccountByIdQuery)
.catch(function(error){
res.status(404).send({ error: "No account found with this Id" });
})
.then(convertDocumentToModel)
.then(verifyOldPassword)
.catch(function(error) {
res.status(406).send({ OldPassword: error });
})
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch(function(error){
console.log(error);
res.status(500).send({ error: "Unable to change password" });
});
So the behaviour I am after is:
Goes to get account by Id
If there is a rejection at this point, bomb out and return an error
If there is no error convert the document returned to a model
Verify the password with the database document
If the passwords dont match then bomb out and return a different error
If there is no error change the passwords
Then return success
If anything else went wrong, return a 500
So currently catches do not seem to stop the chaining, and that makes sense, so I am wondering if there is a way for me to somehow force the chain to stop at a certain point based upon the errors, or if there is a better way to structure this to get some form of branching behaviour, as there is a case of if X do Y else Z.
Any help would be great.
This behavior is exactly like a synchronous throw:
try{
throw new Error();
} catch(e){
// handle
}
// this code will run, since you recovered from the error!
That's half of the point of .catch - to be able to recover from errors. It might be desirable to rethrow to signal the state is still an error:
try{
throw new Error();
} catch(e){
// handle
throw e; // or a wrapper over e so we know it wasn't handled
}
// this code will not run
However, this alone won't work in your case since the error be caught by a later handler. The real issue here is that generalized "HANDLE ANYTHING" error handlers are a bad practice in general and are extremely frowned upon in other programming languages and ecosystems. For this reason Bluebird offers typed and predicate catches.
The added advantage is that your business logic does not (and shouldn't) have to be aware of the request/response cycle at all. It is not the query's responsibility to decide which HTTP status and error the client gets and later as your app grows you might want to separate the business logic (how to query your DB and how to process your data) from what you send to the client (what http status code, what text and what response).
Here is how I'd write your code.
First, I'd get .Query to throw a NoSuchAccountError, I'd subclass it from Promise.OperationalError which Bluebird already provides. If you're unsure how to subclass an error let me know.
I'd additionally subclass it for AuthenticationError and then do something like:
function changePassword(queryDataEtc){
return repository.Query(getAccountByIdQuery)
.then(convertDocumentToModel)
.then(verifyOldPassword)
.then(changePassword);
}
As you can see - it's very clean and you can read the text like an instruction manual of what happens in the process. It is also separated from the request/response.
Now, I'd call it from the route handler as such:
changePassword(params)
.catch(NoSuchAccountError, function(e){
res.status(404).send({ error: "No account found with this Id" });
}).catch(AuthenticationError, function(e){
res.status(406).send({ OldPassword: error });
}).error(function(e){ // catches any remaining operational errors
res.status(500).send({ error: "Unable to change password" });
}).catch(function(e){
res.status(500).send({ error: "Unknown internal server error" });
});
This way, the logic is all in one place and the decision of how to handle errors to the client is all in one place and they don't clutter eachother.
.catch works like the try-catch statement, which means you only need one catch at the end:
repository.Query(getAccountByIdQuery)
.then(convertDocumentToModel)
.then(verifyOldPassword)
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch(function(error) {
if (/*see if error is not found error*/) {
res.status(404).send({ error: "No account found with this Id" });
} else if (/*see if error is verification error*/) {
res.status(406).send({ OldPassword: error });
} else {
console.log(error);
res.status(500).send({ error: "Unable to change password" });
}
});
I am wondering if there is a way for me to somehow force the chain to stop at a certain point based upon the errors
No. You cannot really "end" a chain, unless you throw an exception that bubbles until its end. See Benjamin Gruenbaum's answer for how to do that.
A derivation of his pattern would be not to distinguish error types, but use errors that have statusCode and body fields which can be sent from a single, generic .catch handler. Depending on your application structure, his solution might be cleaner though.
or if there is a better way to structure this to get some form of branching behaviour
Yes, you can do branching with promises. However, this means to leave the chain and "go back" to nesting - just like you'd do in an nested if-else or try-catch statement:
repository.Query(getAccountByIdQuery)
.then(function(account) {
return convertDocumentToModel(account)
.then(verifyOldPassword)
.then(function(verification) {
return changePassword(verification)
.then(function() {
res.status(200).send();
})
}, function(verificationError) {
res.status(406).send({ OldPassword: error });
})
}, function(accountError){
res.status(404).send({ error: "No account found with this Id" });
})
.catch(function(error){
console.log(error);
res.status(500).send({ error: "Unable to change password" });
});
I have been doing this way:
You leave your catch in the end. And just throw an error when it happens midway your chain.
repository.Query(getAccountByIdQuery)
.then((resultOfQuery) => convertDocumentToModel(resultOfQuery)) //inside convertDocumentToModel() you check for empty and then throw new Error('no_account')
.then((model) => verifyOldPassword(model)) //inside convertDocumentToModel() you check for empty and then throw new Error('no_account')
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch((error) => {
if (error.name === 'no_account'){
res.status(404).send({ error: "No account found with this Id" });
} else if (error.name === 'wrong_old_password'){
res.status(406).send({ OldPassword: error });
} else {
res.status(500).send({ error: "Unable to change password" });
}
});
Your other functions would probably look something like this:
function convertDocumentToModel(resultOfQuery) {
if (!resultOfQuery){
throw new Error('no_account');
} else {
return new Promise(function(resolve) {
//do stuff then resolve
resolve(model);
}
}
Probably a little late to the party, but it is possible to nest .catch as shown here:
Mozilla Developer Network - Using Promises
Edit: I submitted this because it provides the asked functionality in general. However it doesn't in this particular case. Because as explained in detail by others already, .catch is supposed to recover the error. You can't, for example, send a response to the client in multiple .catch callbacks because a .catch with no explicit return resolves it with undefined in that case, causing proceeding .then to trigger even though your chain is not really resolved, potentially causing a following .catch to trigger and sending another response to the client, causing an error and likely throwing an UnhandledPromiseRejection your way. I hope this convoluted sentence made some sense to you.
Instead of .then().catch()... you can do .then(resolveFunc, rejectFunc). This promise chain would be better if you handled things along the way. Here is how I would rewrite it:
repository.Query(getAccountByIdQuery)
.then(
convertDocumentToModel,
() => {
res.status(404).send({ error: "No account found with this Id" });
return Promise.reject(null)
}
)
.then(
verifyOldPassword,
() => Promise.reject(null)
)
.then(
changePassword,
(error) => {
if (error != null) {
res.status(406).send({ OldPassword: error });
}
return Promise.Promise.reject(null);
}
)
.then(
_ => res.status(200).send(),
error => {
if (error != null) {
console.error(error);
res.status(500).send({ error: "Unable to change password" });
}
}
);
Note: The if (error != null) is a bit of a hack to interact with the most recent error.
I think Benjamin Gruenbaum's answer above is the best solution for a complex logic sequence, but here is my alternative for simpler situations. I just use an errorEncountered flag along with return Promise.reject() to skip any subsequent then or catch statements. So it would look like this:
let errorEncountered = false;
someCall({
/* do stuff */
})
.catch({
/* handle error from someCall*/
errorEncountered = true;
return Promise.reject();
})
.then({
/* do other stuff */
/* this is skipped if the preceding catch was triggered, due to Promise.reject */
})
.catch({
if (errorEncountered) {
return;
}
/* handle error from preceding then, if it was executed */
/* if the preceding catch was executed, this is skipped due to the errorEncountered flag */
});
If you have more than two then/catch pairs, you should probably use Benjamin Gruenbaum's solution. But this works for a simple set-up.
Note that the final catch only has return; rather than return Promise.reject();, because there's no subsequent then that we need to skip, and it would count as an unhandled Promise rejection, which Node doesn't like. As is written above, the final catch will return a peacefully resolved Promise.
I wanted to preserve the branching behaviour that Bergi's answer had, yet still provide the clean code structure of unnested .then()'s
If you can handle some ugliness in the machinery that makes this code work, the result is a clean code structure similar to non-nested chained .then()'s
One nice part of structuring a chain like this, is that you can handle all the potential results in one place by chainRequests(...).then(handleAllPotentialResults) this might be nice if you need to hide the request chain behind some standardised interface.
const log = console.log;
const chainRequest = (stepFunction, step) => (response) => {
if (response.status === 200) {
return stepFunction(response, step);
}
else {
log(`Failure at step: ${step}`);
return response;
}
};
const chainRequests = (initialRequest, ...steps) => {
const recurs = (step) => (response) => {
const incStep = step + 1;
const nextStep = steps.shift();
return nextStep ? nextStep(response, step).then(chainRequest(recurs(incStep), incStep)) : response;
};
return initialRequest().then(recurs(0));
};
// Usage
async function workingExample() {
return await chainRequests(
() => fetch('https://jsonplaceholder.typicode.com/users'),
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/'); },
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/3'); }
);
}
async function failureExample() {
return await chainRequests(
() => fetch('https://jsonplaceholder.typicode.com/users'),
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/fail'); },
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/3'); }
);
}
console.log(await workingExample());
console.log(await failureExample());
The idea is there, but the interface exposed could probably use some tweaking.
Seeing as this implementation used curried arrow functions, the above could potentially be implemented with more direct async/await code

Categories