I am new to VueJS but stuck trying to handle a promise after interceptor is triggered.
I have a component called orders (orders/index.vue) and in this component I have a created function as such:
created() {
this.$store.dispatch('user/GETORDERS')
.then(response => {
if (response.status === 200) {
this.data = response.data.data
}
})
}
This dispatch call is mapped to an axios function which calls our API to get the data:
export async function getorders() {
return apiClient
.get('/get-orders')
.then(response => {
return response
})
.catch(err => {
return err.response
})
}
I have an interceptor as such:
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.config.url !== '/login' && error.config.url !== '/logout' && error.response.status === 401) {
store.dispatch('user/HANDLE_401')
return Promise.reject(error)
} else {
throw error
}
})
What I noticed is that when the 401 is triggered (a user is on the app, session expires but they try to navigate to another part of the dashboard, they are logged out as expected (HANDLE_401)). Still, it appears that the created function inside the index.vue component is being triggered after the Promise.reject occurs. I added a console.log() above the if statement in the .then clause to test it. Am I handling the rejection properly? Shouldn't the promise be canceled and no further evaluation occur? Why does the .then trigger if not?
The second then triggers because you are catch-ing the error from the first then. So the first Promise is rejected, you catch this rejection and return err.response - so the second Promise continues normally. If you want your first rejection to propagate through the Promise's chain - you should only add catch on your top-level Promise. Or you should throw an error inside your lower-level catch block(s).
Related
I have two functions, login (in fileB.js):
export const login = async (data) => {
try {
const response = await auth.login(data);
return response;
} catch (e) {
return new Error(e);
}
};
and loginProcess (in fileA.js):
const loginProcess = (data) => {
login(data)
.then((response) => {
if (response.status === 200) {
}
})
.catch((e) => {
setError(true);
});
};
If I have an error inside login() function it returns new Error(e) but inside loginProcess() the error from login() is not caught by catch but with then. I need to catch the new Error from login() inside catch in loginProcess(), how can I fix it?
You are converting promise rejection into promise fulfilment by returning an error object.
Retuning a non-promise value from the catch block will fulfil the promise returned by the login function with the return value of the catch block.
To reject the promise returned by the login function:
Re-throw the error caught by the catch block, or
Remove the try-catch block from the login function and let the calling code handle the error.
login function could be re-written as:
export const login = (data) => {
return auth.login(data);
};
I suggest that you choose the second option and re-write the login function as shown above. There is no need for a catch block that just re-throws the error.
I have two fetch scripts that work great at either or though I can't figure out how to combine them.
This first one allows me to know what the response.status is however even though it somehow knows the server's HTTP response while not having the response body (yeah, asynchronous):
fetch(url).then(function(r)
{
if (r.status != 200) {alert('Error: unable to load preview, HTTP response '+r.status+'.');}
else
{
console.log(r.text());//Promise { <state>: "pending" }, no good.
}
}).catch(function(err) {alert('Error: '+err);});
This second script allows me to access the response.text() though I have no access to the response.status:
fetch(url).then(r => r.text()).then(function(r)
{
console.log(r);//response text.
});
How do I combine the scripts properly so I have access to both the response.status and response.text() after the request has been received?
fetch("https://api.thecatapi.com/v1/images/search").then(function(r)
{
if (r.status != 200) {
alert('Error: unable to load preview, HTTP response '+r.status+'.');
return
}
r.text().then(txt => console.log(txt))
}).catch(function(err) {alert('Error: '+err);});
You can do it like this; Promise need to be resolved before you can access the value
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
You can use Promise.all which allows you to handle many Promise at the same time as fetch return a promise.
const firstFetch = fetch(url);
const secondFetch = fetch(url);
Promise.all([firstFetch, secondFetch]).then(([firstResponse, secondResponse]) => {
// Here you can have access to both firstResponse.status
// And secondResponse.text
})
while #shubhan's code will work, a cleaner approach may be the built in promise chaining, to avoid callback hell which promises strives to solve:
fetch(url)
.then(response => {
if (response.status >= 400) throw { code: response.status }
return response.text() // if you return a promise in a `then` block, the chained `then` block will get the resolved result
})
.then(text => {
console.log(text)
// handle successful event
})
.catch(err => {
// if at any stage of the promise chain, if a promise rejects, or throws, it will be caught by the `catch` block
if (err.code) {
// handle status error
} else {
// handle other errors
}
})
Thx
fetch("https://api.thecatapi.com/v1/images/search").then(function(r)
{
if (r.status != 200) {
alert('Error: unable to load preview, HTTP response '+r.status+'.');
return
}
r.text().then(txt => console.log(txt))
}).catch(function(err) {alert('Error: '+err);});
I am trying to use a proper way to handling my errors in my React app with Axios.
When the connection is dead, I won't get a status code, so I can't check the status code of the response. What is the elegant way to handle errors without a status code?
try {
const res = await axios.get("/login");
} catch (error) {
const {status} = error.response;
if (status === 401) doSomething();
}
I get this error:
[Unhandled promise rejection: TypeError: undefined is not an object
Yes, you are correct. Unhandled promise rejection and uncaughtExceptions are not the same. To handle promise rejection you will need to check for "res" value like so:
try {
const res = await axios.get("/login");
if(res.err) {
// An error exist
// Do handle response error
}
} catch (error) {
const {status} = error.response;
if (status === 401) doSomething();
}
The reason we are checking in the response value is because we are using await. When we use await we will get either the resolved response or the rejected response. It will not throw an exception.
throw Error("This is an exception")
This will be caught when using a wrapper around the code like a try-catch.
Another way of solving this issue is by adding a .catch after the fetch. Like so:
try {
const res = await axios.get("/login").catch(err => console.error(err));
} catch (error) {
const {status} = error.response;
if (status === 401) doSomething();
}
Now if the fetch failed, res will be undefined, because we only returned what console.error returned which is undefined.
I have a working apollo graphql express server. The only problem is express is complaining that I don't have a catch block on a promise I'm using to verify a jwt token:
(node:96074) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch()
I can add a catch block to the promise but it then returns pending instead of rejected when the token is invalidated. Which causes the authentication flow to break as my graphql resolvers rely on that rejection to block access to the db.
Fwiw this is how auth0, who I'm using for auth, recommends setting it up. They just don't mention the UnhandledPromiseRejectionWarning.
The code looks like this:
//server def
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
if (req.headers.authorization) {
const token = req.headers.authorization.split(' ')[1];
//THE PROMISE IN QUESTION
const authUserObj = new Promise((resolve, reject) => {
jwt.verify(token, getKey, options, (err, decoded) => {
if (err) {
reject(err);
}
if (decoded) {
resolve(decoded);
}
});
});
return {
authUserObj
};
}
},
introspection: true,
playground: true
});
//a graphql resolver that gets the rejection via authUserObj and catches the error
addUser: async (parent, args, {authUserObj}) => {
try {
const AuthUser = await authUserObj;
const response = await User.create(args);
return response;
} catch(err) {
throw new AuthenticationError('You must be logged in to do this');
}
}
That all works... except for that nagging node error I wish to vanquish! So I add a catch block to the promise:
const authUserObj = new Promise((resolve, reject) => {
jwt.verify(token, getKey, options, (err, decoded) => {
if (err) {
console.log("-------rejected-------", err.message)
reject(err);
}
if (decoded) {
console.log("-------decoded-------")
resolve(decoded);
}
});
}).catch( err => { return err.message});
And now instead of authUserObj returning rejected it's pending and anyone will be able to add a user, which kind of defeats the purpose of auth.
If anyone knows how to catch that error while still rejecting it I'm all ears. Thanks.
The problem is less about the unhandled promise rejection and more about the unhandled promise in general. You try to put a promise inside the context object and then await the promise in the addUser resolver only. In other resolvers, the promise might not be used at all, and when the jwt verification fails for those the rejection will go unhandled. (Also if the resolvers are executed asynchronously, the promise might be rejected before they can handle it).
Instead, the whole context initialisation should be done asynchronously, returning a promise for the context object with the user details. This means that the request will fail before even starting to execute a query:
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
if (req.headers.authorization) {
const token = req.headers.authorization.split(' ')[1];
return new Promise((resolve, reject) => {
jwt.verify(token, getKey, options, (err, decoded) => {
if (err) reject(err);
else resolve(decoded);
});
}).then(authUser => {
if (authUser) return { authUser };
// else return {};
});
// .catch(err => { … }) - you may chose to ignore verification failure,
// and still return a context object (without an `authUser`)
}
// else return {}; - when not sending the header, no token will be checked at all
},
introspection: true,
playground: true
});
// a graphql resolver that checks for the authUserObj
addUser: async (parent, args, {authUserObj}) => {
if (!authUserObj) { // you might also want to check specific claims of the jwt
throw new AuthenticationError('You must be logged in to do this');
}
const response = await User.create(args);
return response;
}
Just like with try/catch, a .catch() will change the promise chain from rejected to resolved if you just return a normal value from the .catch() handler (or return nothing). When you return a "normal" value, the rejection is considered "handled" and the promise chain becomes resolved with that new value. That's how you handle errors and continue normal processing.
To keep the promise chain rejected, you have to either throw or return a rejected promise. That will keep the promise chain as rejected.
So, if you want authUserObj to keep the promise rejected, then change this:
}).catch( err => { return err.message});
to this:
}).catch( err => { return Promise.reject(err.message)});
or something similar that either throws an error or returns a rejected promise.
I was reading stuffs about promise and fetch and got really confused. I got the following code from Introduction to fetch.
My question is: what happens if status returns a rejected promise? then(json) is chained after then(status), does that mean then(json) won't do anything since then(json) only gets executed when status returnes a resolved promise? Or does that mean the chain just keeps passing all the thens if status returns rejected promise until it reaches reach catch at the bottom, and the catch catches error?
Or if I was wrong, what is the correct interpretation of this code?
function status(response) {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
return Promise.reject(new Error(response.statusText))
}
}
function json(response) {
return response.json()
}
fetch('users.json')
.then(status)
.then(json)
.then(function(data) {
console.log('Request succeeded with JSON response', data);
}).catch(function(error) {
console.log('Request failed', error);
});
In my early days of trying to understand promises, I thought of the .then chain as being two chains ... success and rejection
a rejection or error causes the "execution" to "jump" from success to rejection
if a rejection handler returns a value that isn't a rejected promised, the "execution" will "jump" to the success chain
Note: my earliest exposure to promises had no .catch ... because .then actually accepts two arguments onFullfilled and onRejected - if either of those is not a function it is ignored -
So, your code can be written as follows:
function status(response) {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
return Promise.reject(new Error(response.statusText))
}
}
function json(response) {
return response.json()
}
function log(data) {
console.log(data);
}
function handleError(error) {
console.log('Request failed', error);
}
fetch('users.json')
.then(status, null)
.then(json, null)
.then(log, null)
.then(null, handleError);
Now it's fairly clear that given an error in function stats, the "on rejected" chain is in "effect" (I really need to think of better terminology), and there's nothing in the reject chain until the very bottom, therefore, that's the next code that gets executed
Note .catch in some Promise libraries is simply the following
Promise.prototype.catch = function catch(onRejected) {
return this.then(null, onRejected);
};
When a promise gets rejected it goes directly to the .catch() without executing anything else of the chain.
So if status returns a Promise.reject only the catch function will execute.