function sendPushNotification(subscription, urlEncodedData){
try {
webpush.sendNotification(subscription, urlEncodedData);
} catch (err){
console.log('err');
console.log(err);
}
}
This doesn't catch the error, it is still considered an 'unhandledRejection'. Why?
If you're calling an async function, or a function that returns a Promise then you need to sequence things properly:
async function sendPushNotification(subscription, urlEncodedData){
try {
await webpush.sendNotification(subscription, urlEncodedData);
} catch (err){
console.log('err');
console.log(err);
}
}
Here await will capture any response. An error condition that will manifest as an exception.
Note that this makes sendPushNotification() return a Promise so you will have to treat it as asynchronous. This means the caller needs to await as well, and that may have impacts up the call chain depending on your sequencing requirements.
Related
I have a function that looks like this:
function x(req, res, next){
await doSomething().catch(error =>{
return res.send('error here'})
}
await doSomethingElse().catch(error =>{console.log(error)})
return res.send('success');
}
When doSomething() returns an error, I get "Cannot set headers after they are sent to the client", which I understand, but why doesn't the script terminate when I call the first res.send?
My guess is that it's similar to a callback in that the returned value is ignored, so it sends the response, but ignores the fact that the script should end there.
How can I fix this problem?
It gets very complicated to control flow when you are mixing .catch() and await. I would strongly suggest using only one model at a time. With await, you can do this:
async function x(req, res, next) {
try {
await doSomething();
await doSomethingElse();
res.send('success');
} catch(e) {
console.log(e);
res.send('error here'})
}
}
why doesn't the script terminate when I call the first res.send?
Because the return in that statement just returns back from the .catch() handler not from your main function and since you've done a .catch() and not thrown inside that catch handler or returned a rejected promise from the handler, then the parent promise because resolved and execution after the await continues just fine which then causes you to run into the second res.send() which causes the warning about headers already sent.
if you want to use .then() .catch() just try:
function x(req, res, next) {
doSomething()
.catch((error) => {
return res.send("error here");
})
.then(() => {
doSomethingElse()
.catch((error) => {
console.log(error);
})
.then(() => {
return res.send("success");
});
});
}
I have a store method...
async store() {
try {
return await axios.post('/upload', data);
} catch (error) {
}
},
Called by:
store().then(()=>{ console.log('ok'); }, ()=>{ console.log('not ok'); });
But when the store method fails and an error is caught, the first method in then is always called, how can I get the failed not ok method to be called?
You need to throw the error caught in the catch block of the store function
async store() {
try {
return await axios.post('/upload', data);
} catch (error) {
throw error;
}
}
You could also skip catching the error in the store function and simply catch it when store function is called. To do this, you just need to return the result of axios.post(...).
async store() {
return axios.post('/upload', data);
}
(Note that, without the try-catch block, you don't need an await before axios.post(...) because the promise returned by the store function will be resolved to the promise returned by axios.post(...). This means that if the promise returned by axios.post(...) is fulfilled, promise returned by the store function will also fulfil with the same fulfilment value with which the promise returned by axios.post(...) fulfilled.)
It is uncommon to pass second argument to then function. Instead, you should chain a .catch() block to catch the error.
store()
.then(() => console.log('ok'))
.catch(() => console.log('not ok'));
I have this code:
on('connection', async(socket) => {
try {
const promises = members.map(async(member) => {
try {
const users = await User.find({})
const _promises = users.map(async() => {
try {
//some code here
} catch (err) {
console.log(err)
}
})
await Promise.all(_promises)
} catch (err) {
console.log(err)
}
})
await Promise.all(promises)
} catch (err) {
console.log(err)
throw err
}
})
As you can see I have a try catch for each nested async function. Would it be possible to tweak the code to use only a single catch, or to simplify things somehow?
You can have just a single await at the top level of the handler. If the Promises spawned inside it all chain together to a Promise which is awaited at the top level (like in your example), that top await will catch errors thrown anywhere inside.
But, your catch section should not throw another error unless .on handles Promise rejections as well, otherwise you'll get an unhandled Promise rejection:
on('connection', async(socket) => {
try {
const promises = members.map(async(member) => {
const users = await User.find({})
const _promises = users.map(async() => {
//some code here which may throw
});
await Promise.all(_promises);
});
await Promise.all(promises);
} catch (err) {
console.log(err);
}
})
If await User.find throws, then the promises array will contain a Promise which rejects, which mean that the top await Promise.all(promises); will throw and be caught
If something inside users.map throws, _promises will contain a Promise which rejects, so await Promise.all(_promises); will throw. But that's inside the const promises = members.map callback, so that will result in promises containing a rejected Promise too - so it will be caught by the await Promise.all(promises); as well.
If a function you call throws an error, the error will fall back to the nearest catch block the function call is enclosed in.
try {
throw "an error";
}
catch(e) {
console.log(e); //output: "an error"
}
Now consider this
try {
try {
throw "an error";
}
catch(e) {
console.log(e); //output: "an error"
}
}
catch(e) {
console.log(e); //This line is not going to be executed
}
This mechanism enables attachment of more error information to the generated error in each level. Imagine your error is an object and each nested catch bock attaching its own information to the error object an pass it along by throwing again.
Look at the following code:
try {
try {
throw {internalError: 101};
}
catch(e) {
//Attach more info and throw again
e.additionalInfo = 'Disconnected when gathering information';
throw e;
}
}
catch(e) {
e.message = 'Cannot get information'
console.log(e); //Final error with lot of information
}
I'm trying to do some validations before creating/updating an entry as shown below:
async save(){
return new Promise((resolve, reject)=>{
if(!this.isCampaignValid){
this.handleError()
reject()
}
else{
this.$store
.dispatch('updateCampaign')
.then((res)=>{
resolve()
this.showNotification(res.message, 'success')
})
.catch(error=>{
this.showNotification(error.message, 'error')
reject()
})
}
})
},
the isCampaignValid is a computed value which computes the validity.
If the campaign is not valid, then I'm getting an error in the console as below:
Uncaught (in promise) undefined
The this.handleError() function works too. How can handle this promise error situation?
Just in case handleError() throws, try:
if (!this.isCampaignValid) {
try {
this.handleError()
} catch (e) {
console.error(e);
}
reject()
}
First of all, you don't need to return a promise in an async function. It implicitly returns one, resolving with the value returned by the function or rejecting with the error object if the function throws. Although you could return a promise and JS unpacks it for you, it's unneeded code.
That said, because async returns a promise, you'll have to catch that promise too. Since your first conditional block just throws an error but doesn't catch it, the promise returned by save will reject. You need to handle that rejection.
Here's a simplified version of your code to see where it's happening.
async save(){
if(!this.isCampaignValid){
this.handleError()
// Throwing an error in an async function is equivalent to a reject.
throw new Error('Campaign is not valid') // Here
}
else{
try {
const res = await this.$store.dispatch('updateCampaign')
this.showNotification(res.message, 'success')
} catch (e) {
this.showNotification(error.message, 'error')
}
}
},
// When you call save, catch the error
yourObject.save()
.then(() => {...})
.catch(() => {...})
// If your call is in an async function, you can try-catch as well
try {
await yourObject.save()
} catch(e) {
// It failed.
}
Recently I embraced promises chaining pattern. It's very convenient to do like this:
action1
.then(()=> action2())
.then(()=> action3());
But, in order to do it I changed all my methods like this (TypeScript):
action1() : Promise<any>{
try{
// actual code
return Promise.resolve();
} catch (err){
console.error(err);
return Promise.reject(err);
}
}
This looks like very repetitive pattern. What's the best way to avoid code duplication?
Write a function to wrap promise over a function and you can reuse it
wrapPromise(fun) : Promise<any>{
try{
var value = fun()
return Promise.resolve(value);
} catch (err){
console.error(err);
return Promise.reject(err);
}
}
wrapPromise(action1).then()
Since you are using typescript, you'll want to use async/await. Simply do
async action1(): Promise<any>{
try {
return // actual code;
} catch (err){
console.error(err);
throw err;
}
}
However there's a good chance you actually don't want to catch, log and rethrow all errors in every function, so this simplifies to
async action1(): Promise<any>{
return // actual code;
}