Chaining promises with then and catch - javascript

I'm using the bluebird Promise library. I'd like to chain promises and catch specific promises errors. Here's what I'm doing :
getSession(sessionId)
.catch(function (err) {
next(new Error('session not found'));
})
.then(function (session) {
return getUser(session.user_id);
})
.catch(function (err) {
next(new Error('user not found'));
})
.then(function (user) {
req.user = user;
next();
});
But if an error is thrown by getSession, the two catch are called, as well as the second then. I'd like to stop the error propagation at the first catch, so that the second catch is only called when getUser throws, and the second then when getUser succeeds. What do?

The promise that is returned by the .catch method will still be resolved with the result of the callback, it doesn't just stop the propagation of the chain. You will either need to branch the chain:
var session = getSession(sessionId);
session.catch(function (err) { next(new Error('session not found')); });
var user = session.get("user_id").then(getUser);
user.catch(function (err) { next(new Error('user not found')); })
user.then(function (user) {
req.user = user;
next();
});
or use the second callback to then:
getSession(sessionId).then(function(session) {
getUser(session.user_id).then(function (user) {
req.user = user;
next();
}, function (err) {
next(new Error('user not found'));
});
}, function (err) {
next(new Error('session not found'));
});
Alternatively, the better way would to just propagate the errors through the chain, and call next only in the very end:
getSession(sessionId).catch(function (err) {
throw new Error('session not found'));
}).then(function(session) {
return getUser(session.user_id).catch(function (err) {
throw new Error('user not found'));
})
}).then(function (user) {
req.user = user;
return null;
}).then(next, next);

Since you're using bluebird for promises, you actually don't need a catch statement after every function. You can chain all your thens together, and then close the whole thing off with a single catch. Something like this:
getSession(sessionId)
.then(function (session) {
return getUser(session.user_id);
})
.then(function (user) {
req.user = user;
next();
})
.catch(function(error){
/* potentially some code for generating an error specific message here */
next(error);
});
Assuming the error messages tell you what the error is, it's still possible to send an error specific message like 'session not found' or 'user not found', but you'll just have to look into the error message to see what it gives you.
Note: I'm sure you probably have a reason for calling next regardless if there's an error or not, but it might be useful to throw in a console.error(error) in the case that you get an error. Alternatively, you could use some other error handling function, whether it's a console.error, or res.send(404), or something of the like.

I am using it like that:
getSession(x)
.then(function (a) {
...
})
.then(function (b) {
if(err){
throw next(new Error('err msg'))
}
...
})
.then(function (c) {
...
})
.catch(next);

Related

Why isn't try catch blocks catching exceptions

So I have this piece of code that works fine, but if there is an error with the data type the try catch blocks are not working. It appears I am going to have to check all data types before using them to ensure my server doesn't crash.
For instance in the following code, If tokens for some reason wasn't an array, when the error get's thrown, my try catch isn't catching the error. Instead it is crashing the server.
router.patch("/updateDeviceToken", checkIfAuthenticated, (req, res) => {
try {
console.log("In updating contacts");
console.log("body", req.body);
User.findById(req.authId, (error, user) => {
try {
if (error) {
console.log(error);
console.log("user found");
return res.status(404).json(error);
}
if (!user) return res.status(401).json("User not found");
if (
req.body.android &&
!user.tokens.android.includes(req.body.android)
) {
user.tokens.android.push(req.body.android);
user
.save()
.then((user) => {
console.log("token updated");
return res.json("Token updated");
})
.catch((error) => res.status(404).json(error));
} else if (req.body.iOS && !user.tokens.iOS.includes(req.body.iOS)) {
user.tokens.iOS.push(req.body.iOS);
user
.save()
.then((user) => {
console.log("token updated");
return res.json("Token updated");
})
.catch((error) => res.status(404).json(error));
} else {
console.log("token received but not updated");
return res.json("Token received but not updated");
}
} catch (e) {
console.log(e);
return res.status(404).json("There was an error");
}
});
} catch (e) {
console.log(e);
}
});
try catch doesnt work well with then
Can you change code to use promises with await / async ?
You can use try catch with it
I struggled with that as it is easily overlooked in the documentation but For errors returned from asynchronous functions invoked by route handlers and middleware, you must pass them to the next() function, where Express will catch and process them. For example:
app.get('/', function (req, res, next) {
fs.readFile('/file-does-not-exist', function (err, data) {
if (err) {
next(err) // Pass errors to Express.
} else {
res.send(data)
}
})
})
You can also write your custom error handler, long read but it will save you a lot of time.

call the same http request if it fails but with different parameter to get default data

does it make sense to call the same http request call within the catch if the first one fails but with different parameter in order to return some default data ?
var defaultData = false;
clientService.getClients(defaultData)
.then(function (res) {
//do something
}).catch(function (err) {
defaultData = true;
clientService.getClients(defaultData)
.then(function (res) {
//do something
}).catch(function (err) {
console.log(err)
});
});
or this is a bad way ?
Be sure to return the new promise to the catch handler. The code will then chain properly and it avoids nesting:
clientService.getClients({defaultData:false})
.catch(function (err) {
return clientService.getClients({defaultData: true})
}).then(function (res) {
//return something
}).catch(function (err) {
console.log(err)
//IMPORTANT re-throw err
throw err;
});
This issue not bad but I think you have to find reason of the failure and on the furniture do some action.
the best is that you have to create an error handler like:
errorHandler(error:any){
///.....
}
In this method you should check status code of response, for instance if it is 500, you can't call again. or some thing like this.

Promise not returning any data for fetch return

I´m building an express router that uses mongoose to access the database. My current problem relies on this piece of code:
app.use("/authreset", (req, res) => {
authenticator
.resetPassword(
req.body.username,
req.body.password,
req.body.token,
req.body.type
)
.then((response, error) => {
if (error) throw new Error(error);
console.log('*****************');
console.log(response);
if (!response) {
res.sendStatus(401);
return;
}
})
.catch(error => {
console.log('*****************');
console.log(error);
if (error) throw new Error(error);
});
});
resetPassword uses the following mongoose call:
return UserModel
.findByIdAndUpdate(user.id, data, { new: true })
.exec();
For some reason, my route is being called and the response is fine (checked on console.log(response) inside promise).
My problem is that the response is never sent back to the client, that times out the fetch call.
Why is my promise not returning data?
Uh, you log the response, but you never send it (or at least respond with a status code)?
Your code should look more like
app.use("/authreset", (req, res) => {
authenticator.resetPassword(
req.body.username,
req.body.password,
req.body.token,
req.body.type
).then(response => {
console.log(response);
if (!response) {
return res.sendStatus(401);
} else {
return res.sendStatus(200); // <<<
}
}, error => {
console.log(error);
return res.sendStatus(500);
});
});
Notice that the then callback never gets called with more than one argument, so that error you were checking for cannot happen. In the catch handler, you should never rethrow an error if it doesn't get handled further down. Also I changed .then(…).catch(…) to the more fitting .then(…, …).

How to throw exception for sync callback in nodejs?

I want to throw an error exception for an redis.set callback and catch in try-catch block and then get control to error handling express middleware.
try {
redis.get('key', (err, reply) => {
if(err) throw err;
if(!reply) throw new Error('Can't find key');
});
}
catch{
next(error);
}
the problem is, that try-catch is simply not working, error is going to node console, but server is responding with 200 status.
You cant catch async events. Use promises for that:
const getKey = new Promise((res,rej) => {
redis.get('key', (err, reply) => {
if(err) return rej(err);
res(reply);
});
});
So one can do:
getKey.catch(next);
getKey.then(reply => {
//do whatever
next();
});

Nodejs Q promise catch never called with passport js

I'm using the passport library for node to assist me in user signup. I'm also using the 'Q' promise library to avoid the pyramid of doom as I go through several asynchronous steps.
Everything works fine, until I throw an error from one of the promised steps. I would have assumed execution would drop into the .fail function, where I could handle the error and return out of passport with a failed signup. But for a reason that I don't understand, the .fail function never gets called. Instead, I just get a stackdump in my browser window and in my console.
The controlling code is here:
q.fcall(checkEmailIsFree({'email':email, 'username':username, 'password':password}))
.then(checkUsernameIsFree)
.then(registerUser)
.then(function (user) {
if (user) {
logDebug('REGISTERED: ' + email);
return done(null, user);
}
else {
logDebug('Could not register');
return done(null, false);
}
})
.fail(function (err) {
logError('I never get here');
return done(null, false);
})
.done();
And here's how I'm throwing, from within checkEmailIsFree
var error = new Error('Bad times. Email is in use: ' + email);
throw error;
Is there some overall express / node code somewhere that is set to fast dump out an exception somehow? Why isn't my catch being called? What am I missing?
I think done(onFulfilled, onRejected, onProgress) can help in this case. You can try:
q.fcall(checkEmailIsFree({'email':email, 'username':username, 'password':password}))
.then(checkUsernameIsFree)
.then(registerUser)
.then(function (user) {
if (user) {
logDebug('REGISTERED: ' + email);
return done(null, user);
}
else {
logDebug('Could not register');
return done(null, false);
}
})
.done(undefined, function (err) {
logError('Error!'); // used in case of reject
return done(null, false);
});

Categories

Resources