I keep getting this error in the console log
Uncaught (in promise) TypeError: Failed to execute 'fetch' on 'ServiceWorkerGlobalScope': Cannot construct a Request with a Request object that has already been used.
I tried changing my service worker but it doesn't work
self.addEventListener('install', (event) => event.waitUntil(preLoad()));
const preLoad = function () {
return caches.open('cc-offline').then((cache) => {
return cache.addAll(['/offline.html', '/index.html']);
});
}
self.addEventListener('fetch', (event) => {
event.respondWith(checkResponse(event.request).catch(function () {
return returnFromCache(event.request)
}));
event.waitUntil(addToCache(event.request));
});
const checkResponse = (request) => {
return new Promise((fulfill, reject) => {
fetch(request).then((response) => {
(response.status !== 404) ? fulfill(response) : reject()
}, reject)
});
};
const addToCache = (request) => {
return caches.open('cc-offline').then((cache) => {
return fetch(request).then((response) => {
return cache.put(request, response);
});
});
};
const returnFromCache = (request) => {
return caches.open('cc-offline').then((cache) => {
return cache.match(request).then((matching) => {
return (!matching || matching.status == 404) ? cache.match('offline.html') : matching
});
});
};
fetch don't allow you to use a request twice, at least at current version :). Using the same request object in both checkResponse and addToCache maybe the case. You can try to clone the request object before calling fetch as mention in here Why does this code fail to execute 'fetch'?
Related
I have a function that looks like following
export const checkForAvailableAgent = (topicId, serviceUrl, serviceId) => {
const serviceInfo = new window.adiaLive.ServiceInfo({
topicId: topicId, // set here the topicId which you want listen for
OnError: e => {
// react to error message (optional)
console.log("error: ", e);
},
OnServiceStateChange: e => {
if (e.ConnectedAdvisers > 0) {
// there are advisers online for given topicId
console.log("studio available");
return true;
} else {
console.log("studio not available");
return false;
}
}
});
serviceInfo.connect(serviceUrl, serviceId);
};
however the return statements don't return anything when I use the function in the following manner
useEffect(() => {
const agent = checkForAvailableAgent(
`sales_${i18n.language}`,
"https://linktoserviceurl",
"serviceid"
);
// console.log("studio available is: ", agent);
}, []);
the console.log massages appear but the return statement is undefined.
any help would be appreciated.
You can not return from a callback function, as it is running asynchronously and you are not waiting for it to have a result ready.
You can however make the function itself async by returning a Promise instead of the actual result and wait until the Promise has a result ready (e.g. it is resolved):
export const checkForAvailableAgent = (topicId, serviceUrl, serviceId) => {
return new Promise((resolve, reject) => {
const serviceInfo = new window.adiaLive.ServiceInfo({
topicId: topicId, // set here the topicId which you want listen for
OnError: e => {
// react to error message (optional)
console.log("error: ", e);
reject(); // reject on failure
},
OnServiceStateChange: e => {
if (e.ConnectedAdvisers > 0) {
// there are advisers online for given topicId
console.log("studio available");
resolve(true); // resolve instead of return
} else {
console.log("studio not available");
resolve(false);
}
}
});
serviceInfo.connect(serviceUrl, serviceId);
})
};
useEffect(() => {
checkForAvailableAgent(
`sales_${i18n.language}`,
"https://linktoserviceurl",
"serviceid"
).then((agent) => { // then callback is called when the promise resolved
console.log("studio available is: ", agent);
}).catch(error => { // catch is called when promise got rejected
console.log('An error happened');
});
}, []);
The function servceInfo.OnServiceStateChange is a function into the object (seems to be an event).
I'd suggest declaring a variable on the checkForAvailableAgent like connected and change it's value when the event is called.
Then access it using checkForAvailableAgent.connected.
A version with async/await and try/catch
export const checkForAvailableAgent = (topicId, serviceUrl, serviceId) => {
return new Promise((resolve, reject) => {
const serviceInfo = new window.adiaLive.ServiceInfo({
topicId: topicId,
OnError: reject,
OnServiceStateChange: e => resolve(e.ConnectedAdvisers > 0)
});
serviceInfo.connect(serviceUrl, serviceId);
})
};
useEffect(() => {
(async () => {
try {
const isAvailable = await checkForAvailableAgent(
`sales_${i18n.language}`,
"https://linktoserviceurl",
"serviceid"
);
// console.log("Result", isAvailable)
} catch(e) {
console.error(e)
}
})()
// console.log("studio available is: ", agent);
}, []);
There are 2 possible reasons
you are not returning anything from checkForAvailableAgent.
After returning from the checkForAvailableAgent, it might be asynchronous function. You can use async & await.
Below you can see my first attempt at creating a service-worker and the problem with this code is that it never returns cached response since cache.match(request) in addUrlToCache function is always returning undefined. Does anyone have any ideas as to why it's not finding cached requests?
import API from 'top-secret'
const PHOTOS_CACHE = 'photos-cache'
const OBJECTS_CACHE = 'objects-cache'
const urlCacheData = [
{
cacheKey: OBJECTS_CACHE,
url: API.apiUrlGetObjects
},
{
cacheKey: PHOTOS_CACHE,
url: API.apiUrlGetPhotos
}
]
function addUrlToCache (request, cacheKey) {
return caches
.open(cacheKey)
.then(cache => cache.match(request))
.then(cachedResponse => {
if (cachedResponse) {
return cachedResponse
}
return fetch(request).then(response => {
caches.open(cacheKey).then(cache => cache.put(request, response))
return response.clone()
})
})
}
function clearCache () {
return caches.keys().then(cacheNames => {
const promisesToDeleteCache = cacheNames.map(cacheName =>
caches.delete(cacheName)
)
return Promise.all(promisesToDeleteCache)
})
}
self.addEventListener('activate', event => {
event.waitUntil(clearCache())
})
self.addEventListener('fetch', event => {
const urlToCache = urlCacheData.find(item =>
event.request.url.includes(item.url)
)
if (urlToCache) {
event.respondWith(
addUrlToCache(event.request, urlToCache.cacheKey)
)
}
})
After you fetch request in your addUrltoCache function replace
fetch(request).then(response => {
caches.open(cacheKey).then(cache => cache.put(request, response))
return response.clone()
})
with,
return fetch(request).then(response => {
caches.open(cacheKey).then(cache => cache.put(request, response.clone()))
return response;
})
because you should clone first and then return the response.In your code you have already used your response to put value in cache.
I've just figured out that the problem was that I had JSONP requests with random callback values like bla-bla/api?callback=jsonp_randomNumber, so url would be different every time I make a request because of the random number thing, that's why cache.match check wouldn't work.
I fixed it by hardcoding callback value in the config of the jsonp library that I used (jsonp-fetch in my case).
I'm making a post request with a good amount of data that will take about a minute to finish saving. The hosting service I'm using for this will time out requests after 5 seconds, so I need to set this up to periodically check if the data saving is complete to give a final update.
I'm using request-promise, and have looked at both setTimeout and setInterval approaches. In my latest attempt (below) I'm using a setTimeout approach, but my second then keeps being called pretty much immediately. I want this to hang out in the first then stage until it's checked a bunch of times (24 here) or actually finished.
I might have a totally wrong approach here, but I'm not finding examples of the thing I'm trying to reference. Any direction to a good example of this or where I'm going wrong would be greatly appreciated.
const request = require('request-promise');
function checkFiles () {
return request({
uri: `${process.env.ROOT_URL}/api/v1/get/file-processing`,
method: 'GET',
json: true
})
.then(res => { return res; })
.catch(err => { return err; });
}
async function init () {
const filesPostOptions = {/* request options */};
await request(filesPostOptions)
.then(async status => { // THEN #1
if (status.status === 201) {
return status;
}
let checks = 0;
const checkIt = function() {
checks++;
checkFiles()
.then(res => {
if (res.status === 201 || checks > 24) {
status = res;
return status;
} else {
setTimeout(checkIt, 5000);
}
})
.catch(err => {
console.error(err);
});
};
checkIt();
})
.then(status => { // THEN #2
if (!status.status) {
throw Error('Post request timed out.');
}
return status;
})
.catch(err => {
err = err.error ? err.error : err;
console.error(err);
});
}
The post response will deliver a response with a status property (the status code) and a message property.
You need to control the return in "THEN #" by adding a Promise:
.then(async status => { // THEN #1
return new Promise((resolve, reject) => { // <---- prevent an immediate return
if (status.status === 201) {
return resolve(status);
}
let checks = 0;
const checkIt = function() {
checks++;
checkFiles()
.then(res => {
if (res.status === 201 || checks > 24) {
status = res;
resolve(status);
} else {
setTimeout(checkIt, 1000);
}
})
.catch(err => reject(err));
};
checkIt();
})
})
static get(action, params = {}) {
return new Promise((resolve, reject) => {
fetch(UrlHelper.apiUrl(action, params), {
credentials: 'same-origin'
})
.then(response => {
return Api.checkStatus(resolve, reject, response)
})
.then(([ ok, response ]) => {
return ok ? resolve(response) : reject(response);
});
});
}
I don't fully understand what I need to test in this case.
What I need to test here? Promise ?
var request = require("request");
describe("firt get api testing", () => {
test("status code", () => {
request("http://your api", function(
error,
response,
body
) {
var obj = JSON.parse(response.body);
expect(response.statusCode).toBe(200);
expect(obj[0].name).toBe("Karthika Sri");
expect(obj[0].name).toMatch(/[a-z]/);
// expect(response.type).toBe("application/json");
console.log("obj", obj[0].name);
});
});
});
The first chunk as an action creator below works as is with thunk, but I want to also apply the 2nd chunk, which is a promise middleware. How do I tweak it so that it can dispatch 2 actions?
export const fetchPokemon = function (pokemonName) {
return function (dispatch) {
dispatch({type: 'REQUESTING'})
const requestURL = `http://pokeapi.co/api/v2/pokemon/${pokemonName}/`
return fetch(requestURL)
.then(function (response) {
return response.json()
})
.then(function (data) {
dispatch(receivePokemon(formatPokemonData(data)))
dispatch(fetchPokemonDescription(pokemonName))
})
}
}
middleware
const fetchPromiseMiddleware = store => next => action => {
if (typeof action.then !== 'function') {
return next(action)
}
return Promise.resolve(action).then(function (res) {
if (res.status >= 400) {
throw new Error("Bad response from server")
}
return res.json()
}).then(store.dispatch)
}
I've tried the below but get an error:
store.js:33 Uncaught (in promise) TypeError: (0 ,
_actionCreators.receivePokemon) is not a function
const fetchPromiseMiddleware = store => next => action => {
if (typeof action.then !== 'function') {
return next(action)
}
return Promise.resolve(action).then(function (res) {
if (res.status >= 400) {
throw new Error("Bad response from server")
}
return res.json()
}).then(function (data) {
return store.dispatch(receivePokemon(formatPokemonData(data)))
}).then(function (data) {
return store.dispatch(fetchPokemonDescription(data.name))
})
}
there's not enough code in your question, but it seems when you call receivePokemon(formatPokemonData(data)) in the code you show, receivePokemon isn't a function, now you need to check where is that defined, it probably isn't.