nested fetch for a response in a service-worker - javascript

I'm new to service-worker. I'm following a training of Mobile Web Specialist given by Udacity and I'm using google-chrome for that.
I want to fetch for a response from the network, and if it returns 404 as a status I fetch for another response from the network as well.
This is a code to fetch from the network only once. This code works perfectly:
self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).then(function(response) {
if (response.status === 404) {
return new Response("Whoops, not found");
}
return response;
}).catch(function() {
return new Response("Uh oh, that totally failed!");
})
);
});
I did some updates on this code by throwing an error after getting response.status === 404 and manage it the same way in a try/catch. The updated code is below:
self.addEventListener('fetch', function(event) {
try {
event.respondWith(
fetch(event.request).then(function(response) {
if (response.status === 404) {
throw (Error);
}
return response;
}).catch(function() {
return new Response("Uh oh, that totally failed!");
})
);
} catch (Error) {
event.respondWith(
fetch('/imgs/dr-evil.gif').then(function(response) {
if (response.status === 404) {
return new Response('couldn\'t fetch twice');
}
return response;
}).catch(function() {
return new Response("Uh oh, that totally failed twice!");
})
);
}
});
I know there is a better way to do a nested fetch using the service-worker, but I want to know what I did wrong here.

I've not run this so it's possible it needs some adjustments, but try something like this. The problem with your current code is that the first fetch promise chain always resolves to a Response. Either in the first then or in the first catch, where you return a response of "Uh oh, that totally failed!". The event.respondWith takes that response and happily goes along it's way.
The outer try/catch exists in a synchronous space, where as the fetch kicks off an asynchronous chain, so there will be no way for your code to reach the outer catch since it's not in the execution context for the fetch.
If the compatability is the same for both service worker and async/await (I don't know) you might want to take a look at that as it would be a much friendlier way to structure your code.
self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).then(function(response) {
if (response.status === 404) {
throw (Error);
}
return response;
}).catch(function() {
return fetch('/imgs/dr-evil.gif').then(function(response) {
if (response.status === 404) {
throw (Error);
}
return response;
})
}).catch(function() {
return new Response("Uh oh, that totally failed twice!");
})
);
});

Related

Fetch URL call shows Promise { <state>: "pending" }

Why this url fetch isn't working?
Actually this GET method is passing some error message to be stored:
fetch('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', {
method: 'get'
}).then(function(response) {
}).catch(function(err) {
// Error :(
});
However if I typein same URL ( http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha ) in browser it works.
Some other places in WebExension it's working properly.
But not working in another place. Also when I enter in Firefox console too it does not work. It shows some "pending.."
This function too is showing the same behavior:
function ff_httpGetAsync(theUrl, callback, failed_cb) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
// console.log("Successfully downloaded the ajax page");
if (callback) {
if (xmlHttp.responseURL == theUrl) {
callback(xmlHttp.response);
} else {
console.log("diff response url received" + xmlHttp.responseURL);
}
}
} else {
// console.log("Got status =", xmlHttp.status);
}
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
console.log("Gettiy :" + theUrl);
xmlHttp.send(null);
}
ff_httpGetAsync('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', function() {
}, function() {});
I've checked the server. In this case backend pushlo90.php isn't getting called.
Not sure what is wrong with my URL?
That result tells you the promise isn't answered yet. It might work in some occasions when the promise is handled very quickly, before the page is rendered.
Using a promise you basically say 'promise me you will do this'. This promise is either resolved or rejected. Before it's resolved or rejected, it's always pending.
Adding some logging in your first function should explain.
fetch('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', {
method: 'get'
}).then(function(response) {
console.log(response) //do something with response data the promise gives as result
}).catch(function(err) {
console.log(err)// Error :(
});
If you don't want to use the .then(), use async/await.
const functionName = async () => {
const result = await fetch(
"http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha",
{
method: "get"
}
);
console.log(result); //this will only be done after the await section, since the function is defined as async
};
functionName();
The fetch function return a promise that when resolved returns a HTTP response. You then can access the HTTP response Example:
fetch(`https://baconipsum.com/api/?type=all-meat&paras=2&start-with-lorem=1`)
.then(response => {
// HTTP response which needs to be parsed
return response.json()
})
// accessing the json
.then(json =>console.log(json))
So the question you have to ask yourself is: what is returned from the call??
Also be aware that 404 and other HTML error codes wont lead to a reject of the promise so don't bother with catch.
For more details see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
So, the code block that is shown in your question is -
fetch('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', {
method: 'get'
}).then(function(response) {
}).catch(function(err) {
// Error :(
});
So, what it says is fetch module will send a GET request to the URL provided in the request and the response or the error will go into the respective chained functions.
The error could be 404(Not found) or 401(Unauthorized) etc.
To check the error put some logging into your HTTP request handlers.
fetch('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', {
method: 'get'
}).then(function(response) {
console.log(`Response is {response}`)
}).catch(function(err) {
console.log(`Error is {err}`)
})
And for your other code here is the screenshot of what is getting returned from your code -
Where it clearly states 404 (Not found), hence your code will go in the error handler.

Axios override, get status code from the data response instead of status

I'm calling an API that defines the statusCode from data instead of the response code:
{
data: {
statusCode: 422,
message: "User's not found"
},
status: 200
}
In my axios get request it's getting the status code from the status instead in data.
return axios.get(`${process.env.BASE_URL}/users`)
.then(response => {
console.log(response);
}).catch(err => {
console.log(err.message);
});
I'm getting the response but it should go to catch since it's 422.
How can I refer to the statusCode of the data response so that if it's not 200 it should go to catch statement
You can intercept the response, inspect the data and throw a custom error in this case:
// Add a response interceptor
axios.interceptors.response.use(function(response) {
if (response.data && response.data.statusCode && !(response.data.statusCode >= 200 && response.data.statusCode < 300)) throw new Error()
return response;
}, function(error) {
return Promise.reject(error);
});
// Make a GET request
axios.get(url)
.then((data) => {
console.log('data', data)
})
.catch((e) => {
console.log('error', e)
})
This way you configure your axios instance so you dont have to repeat yourself for every single request in your app
Also, you can override the status using following code. But since status validation has already executed, it will not throw errors on bad status codes
// Add a response interceptor
axios.interceptors.response.use(function(response) {
if (response.data && response.data.statusCode) response.status = response.data.statusCode
return response;
}, function(error) {
return Promise.reject(error);
});
You can handle with standard if statement inside the .then()
return axios.get(`${process.env.BASE_URL}/users`)
.then(response => {
if(response.data.statusCode===442){
...//custom error handling goes here
}else{
...//if statusCode is a success one
}
}).catch(err => {
console.log(err.message);
});
Check the response.data.statusCode value, if it is 442 then you should ideally throw an Error and let it be handled in the .catch callback.
return axios.get(`${process.env.BASE_URL}/users`)
.then(response => {
if(response.data.statusCode===442){
throw new Error(response.data.message); //using throw instead of Promise.reject() to break the control flow.
}else{
//return the data wrapped in promise
}
})
.catch((err) => {
console.log(err.message);
return Promise.reject(err.message);
});

Axios - Prevent .then() from executing on http errors

My problem:
I have set up an interceptor to catch error codes in HTTP responses.
When the JWT expires, I have a code 401 coming back from the server. Here's my interceptor:
this.axios.interceptors.response.use(undefined, (error) => {
if (error.response.status === 401) {
this.$store.dispatch('auth/logout').then(() => {
this.$router.push({name: 'login'})
return Promise.reject(error)
})
}
})
My interceptor works fine, except the request that is being intercepted still resolves into the .then() part.
this.axios.get('/texts').then(function(){
// This is still being executed and generates javascript errors because the response doesn't contain the right data
})
From the axios documentation, I found out you can prevent this by calling
this.axios.get('/texts').then(function(){
// only on success
}).catch(function(){
// only on errors
}).then(function(){
// always executed
})
But this is pretty verbose and I don't want to do this on every request that my app makes.
My question is:
How do I prevent axios from executing the .then() callback when I have an error. Is it something I can do in the interceptor? Like event.stopPropagation() or something like that?
You can prevent the Axios Error by using the below set of code
this.axios.interceptors.response.use(undefined, (error) => {
if (error.response.status === 401) {
this.$store.dispatch('auth/logout').then(() => {
this.$router.push({name: 'login'})
return new Promise(() => { });
})
} else {
return Promise.reject(error)
}
})
Throw an exception from catch block to prevent 'then' block
this.axios.get('/texts').then(function(){
// only on success
}).catch(function(e){
// only on errors
throw e;
}).then(function(){
// Will not executed if there is an error but yes on success
})
Did you try catch in the end of the chain? You will get following
this.axios.get('/texts').then(function(){
// only on success
}).then(function(){
// only on success in previous then
}).catch(function(){
// executes on every error from `get` and from two previous `then`
})

Proper mindset for use of promises?

I've only recently looked at promises (JS not being my forte) and I'm not sure what the proper way to do this is. Promises are supposed to prevent right-drifting code but when I end up with somewhat complex logic I end up nested far too deep anyway, so I'm convinced I'm doing it wrong.
If I'm returning both successes and failures as json values, and I want to handle malformed json as well, I immediately think to do something like this:
fetch('json').then(function (result) {
return result.json();
}).catch(function (result) {
console.error("Json parse failed!");
console.error(result.text);
}).then(function (wat) {
// if (!result.ok) { throw...
}).catch(function (wat) {
// Catch http error codes and log the json.errormessage
});
Of course, this won't work. This is stereotypical synchronous code. But it's the first thing that comes to mind. Problems I can see:
How do I get both the response and the json output?
How do I get separate control flow for errors and successes?
How do I catch a json parse error on both types of response?
My best attempt involves nesting to the point where I might as well be using callbacks, and it doesn't work in the end because I still haven't solved any of the above problems:
fetch('json').then(function (response) {
if (!response.ok) {
throw response;
}
}).then(
function (response) {
response.json().then(function (data) {
console.log(data);
});
},
function (response) {
response.json().then(function (data) {
console.error(data.errormessage);
});
}
).catch(function () {
console.error("Json parse failed!");
// Where's my response????
});
What's the "Right" way to do this? (Or at least less wrong)
If you want to call response.json() anyway (for successful and failed response) and want to use the response together will the response data. Use Promise.all:
fetch('json')
.then(response => Promise.all([response, response.json()]))
.then(([response, data]) => {
if (!response.ok) {
console.error(data.errormessage);
} else {
console.log(data);
}
})
.catch(err => {
if (/* if http error */) {
console.error('Http error');
} else if (/* if json parse error */)
console.error('Json parse failed');
} else {
console.error('Unknown error: ' + err);
}
});
You shouldn't use exceptions for control flow in Promises any more than you should when not using Promises. That's why fetch itself doesn't just reject the promise for status codes other than 200.
Here's one suggestion, but the answer will necessarily depend on your specific needs.
fetch('json').then(function (response) {
if (!response.ok) {
response.json().then(function (data) {
console.error(data.errorMessage);
});
return ...;
}
return response.json().catch(function () {
console.error("Json parse failed!");
return ...;
});
}).catch(function (e) {
console.error(e);
return ...;
});

Error Handling in HTTP Ajax Call using $fetch Javascript

I tried to handle the Network related issues in HTTP ajax call. So, I temporarily stopped the respective API's service in IIS and I tried to call the shut downed API - http://localhost:1000/GetData.
fetch("http://localhost:1000/GetData")
.then(handleErrors)
.then(function(response) {
return response.Json();
}).catch(function(error) {
console.log(error);
});
I tried the following code too
fetch("http://localhost:1000/GetData")
.then(response => {
if(response) {
if (response.status === 200) {
alert('Super');
return response.json();
} else {
alert('Hai');
return '';
}
} else {
alert('Oooops');
return '';
}
})
.catch(function(error) {
console.log(error);
});
But its failing and directly hitting the catch block without triggering any alert and its throwing an error. Moreover the response.json(); is in Success block, I don't know how its executed.
TypeError: response.json is not a function
Stack trace:
onFetchError/<#http://192.168.4.159:3000/app.0df2d27323cbbeada2cd.js:9946:13
Kindly assist me how to check the Status code and how to handle the Network error (i.e., Network Unavailable 404, etc.,)
Referred website: https://www.tjvantoll.com/2015/09/13/fetch-and-errors/
Based on this issue on Github, you can try to identify error types in catch block instead. So, something like this may work for your case:
fetch("http://localhost:1000/GetData")
.then(response => {
alert("Super");
return response.json();
})
.catch(err => {
const errStatus = err.response ? err.response.status : 500;
if (errStatus === 404){
// do something
} else {
// do another thing
}
});

Categories