Javascript - error handling stopping code - javascript

quick basic question,
When setting up guildmember.timout with discord.js v13, it gives an example with .then(console.log) and .catch(console.error). Using the example, the code will continue after the .catch.
muteMember
.timeout(time, reason)
.catch((error) => {
return errors(client, message, args, "mute", error, true);
});
muteMember.send ...
At the moment it will run the errors function then continue onto the code after the .catch, for example muteMember.send. What's the best way to have it "stop" after it runs what is inside of the .catch? Thank you in advance

You can make it return a falsy if the error occurs, then check if it is falsy, and return if it is.
let isModified = await muteMember
.timeout(time, reason)
.catch((error) => {
errors(client, message, args, "mute", error, true)
return false;
})
if (!isModified) return;

You can use async-await with try-catch:
async function myFunction()
{
try
{
await muteMember.timeout(time, reason)
// throws an error if it fails -- jumps to the catch block
muteMember.send...
}
catch(error)
{
errorcheck = true
errors(client, message, args, "mute", error, true);
// and whatever other error handling you would like
}
}

The return statement only returns out of the #catch callback. Handle the promise with a #then callback, thats where you want your code to run when its successful.
muteMember
.timeout(time, reason)
.catch((error) => {
//error
errorcheck = true
return errors(client, message, args, "mute", error, true);
})
.then(() => {
//success
})

muteMember.timeout() returns a Promise, so any code that you want to run after the promise resolves you should wrap in a then() block:
muteMember
.timeout(time, reason)
.then((member) => {
// code that depends on successfully timing out a member
muteMember.send....
})
.catch((error) => {
// only runs if there's an error
errorcheck = true
return errors(client, message, args, "mute", error, true);
});
You can also use the more modern and readable async/await syntax:
const myAsyncFunction = async () => {
try {
const member = await muteMember.timeout(time, reason);
// code that depends on successfully timing out a member
muteMember.send....
} catch (error) {
// only runs if there's an error
errorcheck = true
return errors(client, message, args, "mute", error, true);
}
}

Related

In JS fetch API promise style, how to get the raw body when the json() function failed

I know this can be solved by writing all codes to async-await style, then can simply write let text = await res.text(); then try catch the JSON.parse(text) and then do decision.
But here I just want to know if there is any way we can achieve that in .then/.catch style.
Consider the below code:
async function test() {
try {
let n = await fetch("https://stackoverflow.com")
.then(res => {
return res.json()
})
.then(data => data.results.length)
.catch(e => {
console.error("Catch 2", e)
})
}
catch (e) {
console.error("Catch 3", e)
}
}
if we execute this function in the browser devtools(F12) with await test(), then there will be an error catch by the "Catch 2" clause. But in the error detail we can only see some logs like JSON parse error.
We cannot see the full text of the response body.
Is there any way that can get the text when the JSON parsing failed?
Your best bet is to look at the response in your devtools' network tab. That will show you the full response.
But if you want to do it in code, you can separate reading the response from parsing it by using the text method instead of the json method, then parsing the text yourself.
The parsing error may be down to the fact you aren't checking for HTTP success. As I noted on my old anemic blog here, fetch only rejects its promise on network errors, not HTTP errors (like 404, 500, etc.). To check for HTTP success, look at the ok or status properties.
Here's the minimal-changes version separating reading the response from parsing it, and checking for HTTP success before reading it at all:
async function test() {
try {
let n = await fetch("https://stackoverflow.com")
.then((res) => {
if (!res.ok) { // ***
throw new Error(`HTTP error ${res.status}`); // ***
} // ***
return res.text(); // ***
})
.then((text) => {
// *** you can look at `text` here in a debugger, or
// *** log it, save it, etc., before parsing below
// *** (which might throw an error)
try {
const data = JSON.parse(text); // ***
return data.results.length;
} catch (error) {
console.error("Parsing error", e);
console.error("Text we were parsing:", text);
}
})
.catch((e) => {
console.error("Catch 2", e);
});
// ...do something with `n`...
} catch (e) {
console.error("Catch 3", e);
}
}
But a couple of things there:
I wouldn't mix async/await with explicit promise callbacks like that.
With that and with your original code, errors will result in n receive the value undefined, because the catch handlers (and my new try/catch block in the then handler) don't return anything.
Instead:
async function test() {
try {
const res = await fetch("https://stackoverflow.com");
if (!res.ok) {
throw new Error(`HTTP error ${res.status}`);
}
const text = await res.text();
// *** you can look at `text` here in a debugger, or
// *** log it, save it, etc., before parsing below
// *** (which might throw an error)
try {
const data = JSON.parse(text);
const n = data.results.length;
// ...do something with `n`...
} catch (error) {
console.error("Parsing error", e);
console.error("Text we were parsing:", text);
}
} catch (e) {
console.error("Catch 3", e);
}
}
Or if you want to respond differently to the parsing error, wrap that bit in a try/catch, etc.
You shouldn't confuse the catch which catching errors in the fetch function itself - with the response errors
fetch("/developer.mozilla.org")
.then(res => {
if (!res.ok) {
console.log("there was an error also here") <= this code also runs
console.log("response is", res);
}
return res.json()
})
.then(data => data.results.length)
.catch(e => {
console.error("Catch 2", e);
})
In your case, you tried converting data -> JSON w/o success, it failed and dropped to the "catch" section.
but to inspect the response - you can dump it in the first section above where I added res.ok
I believe you could do something like this when using promise style Javascript:
const fetchDataPromise = () => {
fetch('https://stackoverflow.com').then((res) => {
res.json().then((jsonData) => {
console.log(jsonData)
}).catch((err) => {
console.error(err)
res.text().then((rawData) => {
console.log(rawData)
}).catch((err) => console.error(err))
})
})
}
Also more intuitive approach would be to use async/await (the trade-off is that you will have to do the API call again):
const fetchData = async () => {
try {
const res = await fetch('https://stackoverflow.com')
const jsonData = await res.json()
console.log(jsonData)
} catch (err) {
try {
console.error(err)
const res = await fetch('https://stackoverflow.com')
const rawData = await res.text()
console.log(rawData)
} catch (rawError) {
console.error(rawError)
}
}
}

Google Cloud Functions - Retry

I have this idempotent function with multiple promises that I wrote for Google Cloud Functions.
I want to have retry enabled since my used API is pretty inconsistent. This requires a rejected promise to be returned when a retry is needed.
Therefore I tried to return a promise.all([]) but that does not terminate/stop the function when one of the promises fails. It then even proceeds to the promise.all().then()? This should only happen when all 4 promises are successful.
Who can point me in the right direction? Does it even make sense what I'm trying?
exports.scheduleTask = functions
.firestore.document("tasks_schedule/{servicebonnummer}")
.onCreate((snap, context) => {
servicebonnummer = snap.data().data.servicebonnummer;
bondatum = snap.data().data.bondatum;
servicestatus = snap.data().data.servicestatus;
tijdstip = snap.data().data.tijdstip;
firestorePromise = null;
firestoreFinish = null;
cashPromise = null;
cashFinish = null;
//Firebase
//firestoreSchedule executes the required promise
//checkFinished points to a database where it checks a boolean for idempotency
//firestoreFinish writes to this database and sets the boolean to true when the promise is successful
if (!checkFinished("tasks_schedule", servicebonnummer, "firestore")) {
firestorePromise = scheduleFirestore(
servicebonnummer,
bondatum,
servicestatus,
tijdstip
)
.then(output => {
firestoreFinish = markFinished(
"tasks_schedule",
servicebonnummer,
"firestore"
);
return output;
})
.catch(error => {
console.error(
"scheduleFirestore - Error connecting to Firestore: ",
error
);
return error;
});
}
//SOAP API
//cashSchedule executes the required promise
//checkFinished points to a database where it checks a boolean for idempotency
//cashFinish writes to this database and sets the boolean to true when the promise is successful
if (!checkFinished("tasks_schedule", servicebonnummer, "cash")) {
cashPromise = scheduleCash(
servicebonnummer,
moment(bondatum),
servicestatus,
tijdstip
)
.then(result => {
if (result[0].response.code === "2") {
cashFinish = markFinished(
"tasks_schedule",
servicebonnummer,
"cash"
);
return result;
}
throw new Error("Validation error, response not successful");
})
.catch(error => {
console.error("scheduleCash - Error connecting to CASH API: ", error);
return error;
});
}
//CHECK PROMISES
return Promise.all([
firestorePromise,
firestoreFinish,
cashPromise,
cashFinish
])
.then(result => {
removeTask("tasks_schedule", servicebonnummer);
return result;
})
.catch(error => {
console.error("scheduleTask - Retry: ", error);
return error;
});
});
If you code:
let somePromise = new Promise(...);
return somePromise.then(funcA).catch(funcB);
Then you are indeed returning a promise. However, since you have handlers for that promise in your code, we need to look at what happens in more detail. Let us assume that somePromise is rejected. This will mean that the catch() handler will be invoked. It is the outcome of that catch handler that will be the ultimate resolution of the returned promise.
If we look at the MDN docs for Promise.catch() we find the following:
The Promise returned by catch() is rejected if onRejected throws an
error or returns a Promise which is itself rejected; otherwise, it is
resolved.
If we look at your code,
catch(error => {
console.error("scheduleTask - Retry: ", error);
return error;
});
And now ask:
Does this code throw an error? Nope ... it has no throw statement in it and hence just returns the values passed in.
Does the code return a Promise? Nope ... it is passed an error value and simply returns that error value which I am pretty sure will not itself be a Promise.
This means that the overall Promise returned is concluded in a resolved state and not a rejected state and hence the overall Cloud Function is considered to have concluded and is not retried.
Your options may be:
catch(error => {
console.error("scheduleTask - Retry: ", error);
throw error;
});
or
catch(error => {
console.error("scheduleTask - Retry: ", error);
return Promise.reject(error);
});
References:
Promise.prototype.catch()
Promise.reject()

How to determine which promise throws error (in a promise chain)

How can I determine which of the three promises caused the error?
The code below is pseudo, but in my actual code I am working with third-party libraries (Stripe and Firebase) and therefore I cannot modify the actual Promises themselves.
I thought that I could check to see if the error argument in the catch contains a specific value (e.g. in Stripe there's a very detailed error object), but surely there must be a better way.
return async.func.one.doIt()
.then(() => {
return async.func.two.doIt();
})
.then(() => {
return async.func.three.doIt();
})
.then(() => {
return { success: true };
})
.catch((error) => {
// How do I know which 'then' caused
// the catch to invoke?
});
You can put '.catch' between each '.then' method to catch error. It should catch closest error that was thrown by any of previous promises, up to next '.catch' method.
you can change your code to this
return func.one.doIt()
.then(() => {
return async.func.two.doIt().then(() => {
return async.func.three.doIt().then(() => {
return { success: true };
}).catch((error) => {
// How do I know which 'then' caused
// the catch to invoke?
});;
}).catch((error) => {
// How do I know which 'then' caused
// the catch to invoke?
});
}).catch((error) => {
// How do I know which 'then' caused
// the catch to invoke?
});

Async/Await error handling

I'm trying to handle a custom error that my async method throws, but the try catch block doesn't work appropriately.
I think the way I'm doing it should work but the error is not caught and the program terminates by displaying it in the terminal.
Here is where it throws the error:
async setupTap(tap) {
const model = this.connection.model('Tap', TapSchema);
await model.findOneAndUpdate({ id: tap.id }, tap, (err, result) => {
let error = null;
if (!result) {
throw new Error('Tap doesn\'t exists', 404);
}
return result;
});
}
Then, the error handling code:
async setupTapHandler(request, h) {
const tapData = {
id: request.params.id,
clientId: request.payload.clientId,
beerId: request.payload.beerId,
kegId: request.payload.kegId,
};
try {
await this.kegeratorApi.setupTap(tapData);
} catch (e) {
if (e.code === 404) return h.response().code(404);
}
return h.response().code(204);
}
Can someone help me?
I also looked at other topics:
Correct Try...Catch Syntax Using Async/Await
How to properly implement error handling in async/await case
You can only use await to successfully wait on an async operation if you are awaiting a promise. Assuming you are using mongoose, I don't know mongoose really well, but it appears that model.findOneAndUpdate() does not return a promise if you pass it a callback. Instead, it executes and puts the result in the callback.
In addition, doing a throw from a callback like this just throws into the database (the code that called the callback) and won't do you any good at all. To have a throw make a rejected promise, you need to either be throwing from the top level of an async function or be throwing from inside a .then() or .catch() handler or inside a promise executor function. That's where throw makes a promise rejected.
The key here is that you want to use the promise interface to your database, not the callback interface. If you don't pass a callback, then it returns a query which you can use .exec() on to get a promise which you can then use with await.
In addition, you weren't building an error object that would have a .code property set to 404. That isn't a property that is supported by the Error object constructor, so if you want that property, you have to set it manually.
I'd suggest this:
async setupTap(tap) {
const model = this.connection.model('Tap', TapSchema);
let result = await model.findOneAndUpdate({ id: tap.id }, tap).exec();
if (!result) {
let err = new Error('Tap doesn\'t exists');
err.code = 404;
throw err;
}
return result;
}
Or, with only one async operation here, there's really not much benefit to using await. You could just do this:
setupTap(tap) {
const model = this.connection.model('Tap', TapSchema);
return model.findOneAndUpdate({ id: tap.id }, tap).exec().then(result => {
if (!result) {
let err = new Error('Tap doesn\'t exists');
err.code = 404;
throw err;
}
return result;
});
}
The function findOneAndUpdate returns a promise so there should be no need for the callback. If a callback is needed and you can't update to a newer version then maybe wrap calls in a promise (under To use a callback api as promise you can do:)
Then you want to set code on error, you can't do that with the constructor.
async setupTap(tap) {
const model = this.connection.model('Tap', TapSchema);
const result = await model.findOneAndUpdate({ id: tap.id }, tap);
if (!result) {
const e = new Error('Tap doesn\'t exists');
e.code = 404;
throw(e);
}
return result;
}
async setupTapHandler(request, h) {
const tapData = {
id: request.params.id,
clientId: request.payload.clientId,
beerId: request.payload.beerId,
kegId: request.payload.kegId,
};
try {
await this.kegeratorApi.setupTap(tapData);
} catch (e) {
if (e.code === 404) return h.response().code(404);
}
return h.response().code(204);
}

Firebase Promise chain terminate in catch block

I need to exit http function if first catch block is reached.
I'm doing following
.then() <- start work related with database
.then()
.then()
.catch(error => { <- Here I want to catch errors with firebase database and exit function.
console.log(error)
response.status(500).send(error)
})
.then() <- Here I want to send FCM message if there was no database errors
.catch(error => {
console.log(error)
response.status(200).send("Success") <- Main work with database was finished. I still want to send http 200 and don't care about FCM errors.
})
.then(() => {
response.status(200).send("Success")
}) <-This catch block should be fired if there was an issue with FCM
Problem is that function continue to run after first catch block. How to stop this chain properly in first catch block? Thanks
Something like this should work, using a combination a propagated values and a top-level sentinel:
let bail = false
doWork()
.then(result => {
console.log(result)
return true // indicate success
})
.catch(error => {
console.error(error)
return false // indicate error
})
.then(isPriorSuccessful => {
if (!isPriorSuccessful) {
bail = true
return null
}
else {
// do more stuff here, return a promise
return doMoreWork()
}
})
.catch(error => {
console.error(error)
})
.then(() => {
if (bail) {
res.status(500).send("NOT OK")
return
}
console.log("Just before the end")
res.send("OK")
})

Categories