I am trying to use service worker with PreloadJS. I have cached the images required and then loading them using the caches.match() function.
When I try to load the image with jquery it works fine, but on loading with preloadJS it gives the following error
The FetchEvent for "someurl" resulted in a network error response: an "opaque" response was used for a request whose type is not no-cors
Although if I load any other image that isn't cached, PreloadJS loads that image properly. The problem is occuring only when I use caches.match.
What might be the reason for this ?
Load Image using preloadjs
var preload = new createjs.LoadQueue({ crossOrigin: "Anonymous" });
function loadImageUsingPreload() {
preload.on('complete', completed);
preload.loadFile({ id: 'someid', src: 'someurl', crossOrigin: true })
};
function completed() {
var image = preload.getResult("shd");
document.body.append(image);
};
Load Image using jquery
function loadImageUsingJquery() {
jQuery("#image").attr('src', 'someurl');
};
Service Worker fetch event
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
if (!response) {
console.log('fetching...' + event.request.url);
return fetch(event.request);
};
console.log("response", response);
return response;
}));
});
The response object when I load using PreloadJS or jQuery:
Response {type: "opaque", url: "", redirected: false, status: 0, ok: false, …}
Found out the mistake ,
Actually during cache.put I was modifying the request object's mode to 'no-cors' if the origin were not the same.
Now during a fetch event, the event's request object had property 'cors' because it was a cross-origin request.
This difference of value of mode was causing the fetch error.
Service worker install event:
var request = new Request(value);
var url = new URL(request.url);
if (url.origin != location.origin) {
request = new Request(value, { mode: 'no-cors' });
}
return fetch(request).then(function(response) {
var cachedCopy = response.clone();
return cache.put(request, cachedCopy);
});
I changed it to :
var request = new Request(value);
var url = new URL(request.url);
return fetch(request).then(function(response) {
var cachedCopy = response.clone();
return cache.put(request, cachedCopy);
});
I have to use the first method for saving cdn's because second method is not working for them and vice-versa for my images hosted on cloudfront .
I will post the reason when I find why this is happening.
Related
I am new to JavaScript frameworks and currently trying to setup a falcor router calling an external api (for now consider it as an express api app + mango db, hosted at 3000 port).
Now, I am able to use the request package (commented out lines) and successfully call the Express Api app (which returns obj.rating = 4). But I am unable to send this value from the falcor router instead of the hard-coded value "5".
Below is the falcor-router's server.js code:
app.use('/rating.json', falcorExpress.dataSourceRoute(function (req, res) {
return new Router([
{
route: "rating",
get: function() {
var obj;
// request('http://localhost:3000/rating/101', function (error, response, body) {
// obj = JSON.parse(body);
// console.log('rating:', obj.rating); // obj.rating = 4
// });
return {path:["rating"], value:"5"};
}
}
]);
}));
The below is the code for index.html:
<script>
function showRating() {
var model = new falcor.Model({source: new falcor.HttpDataSource('http://localhost/rating.json') });
model.
get("rating").
then(function(response) {
document.getElementById('filmRating').innerText = JSON.stringify(response.json,null, 4);
});
}
</script>
I also tried to look at the global variable declaration, synchronize http request calls, promises, then statements etc. But nothing seemed to work, clearly I am missing out something here - not sure what.
The router's get handler expects the return value to be a promise or an observable that resolves to a pathValue. To get your request against the db to work, simply return a promise that resolves to a pathValue, e.g.
return new Router([
{
route: "rating",
get: function() {
return request('http://localhost:3000/rating/101', function (error, response, body) {
return { path: ["rating", value: JSON.parse(body).rating };
});
}
}
]);
The following Node.js code:
var request = require('request');
var getLibs = function() {
var options = { packages: ['example1', 'example2', 'example3'], os: 'linux', pack_type: 'npm' }
request({url:'http://localhost:3000/package', qs:options},
function (error , response, body) {
if (! error && response.statusCode == 200) {
console.log(body);
} else if (error) {
console.log(error);
} else{
console.log(response.statusCode);
}
});
}();
sends the following http GET request query that is received by like this:
{"packages"=>{"0"=>"example1", "1"=>"example2", "2"=>"example3"}, "os"=>"linux", "pack_type"=>"npm"}
How can I optimize this request to be received like this:
{"packages"=>["example1", "example2", "example3"], "os"=>"linux", "pack_type"=>"npm"}
Note. The REST API is built in Ruby on Rails
If the array need to be received as it is, you can set useQuerystring as true:
UPDATE: list key in the following code example has been changed to 'list[]', so that OP's ruby backend can successfully parse the array.
Here is example code:
const request = require('request');
let data = {
'name': 'John',
'list[]': ['XXX', 'YYY', 'ZZZ']
};
request({
url: 'https://requestb.in/1fg1v0i1',
qs: data,
useQuerystring: true
}, function(err, res, body) {
// ...
});
In this way, when the HTTP GET request is sent, the query parameters would be:
?name=John&list[]=XXX&list[]=YYY&list[]=ZZZ
and the list field would be parsed as ['XXX', 'YYY', 'ZZZ']
Without useQuerystring (default value as false), the query parameters would be:
?name=John&list[][0]=XXX&list[][1]=YYY&list[][2]=ZZZ
I finally found a fix. I used 'qs' to stringify 'options' with {arrayFormat : 'brackets'} and then concatinated to url ended with '?' as follows:
var request = require('request');
var qs1 = require('qs');
var getLibs = function() {
var options = qs1.stringify({
packages: ['example1', 'example2', 'example3'],
os: 'linux',
pack_type: 'npm'
},{
arrayFormat : 'brackets'
});
request({url:'http://localhost:3000/package?' + options},
function (error , response, body) {
if (! error && response.statusCode == 200) {
console.log(body);
} else if (error) {
console.log(error);
} else{
console.log(response.statusCode);
}
});
}();
Note: I tried to avoid concatenation to url, but all responses had code 400
This problem can be solved using Request library itself.
Request internally uses qs.stringify. You can pass q option to request which it will use to parse array params.
You don't need to append to url which leaves reader in question why that would have been done.
Reference: https://github.com/request/request#requestoptions-callback
const options = {
method: 'GET',
uri: 'http://localhost:3000/package',
qs: {
packages: ['example1', 'example2', 'example3'],
os: 'linux',
pack_type: 'npm'
},
qsStringifyOptions: {
arrayFormat: 'repeat' // You could use one of indices|brackets|repeat
},
json: true
};
Im having trouble with my Service Worker. I have implemented it with the Cache then Network technique, where content is first fetched from cache, and a network-fetch is always performed and the result is cached at success. (Inspired by this solution, CSS-Tricks)
When I make changes to my web app and hit refresh, I of course, the first time get the old content. But on subsequent refreshes the content alternates between old and new. I can get new or old content five times in a row or it could differ on each request.
I have been debugging the Service Worker for a while now and does not get any wiser. Does anyone have an idea about whats wrong with the implementation?
EDIT:
var version = 'v1::2';
self.addEventListener("install", function (event) {
event.waitUntil(
caches
.open(version + 'fundamentals')
.then(function (cache) {
return cache.addAll([
"/"
]);
})
);
});
self.addEventListener("fetch", function (event) {
if (event.request.method !== 'GET') {
return;
}
event.respondWith(
caches
.match(event.request)
.then(function (cached) {
var networked = fetch(event.request)
.then(fetchedFromNetwork, unableToResolve)
.catch(unableToResolve);
return cached || networked;
function fetchedFromNetwork(response) {
var cacheCopy = response.clone();
caches
.open(version + 'pages')
.then(function add(cache) {
cache.put(event.request, cacheCopy);
});
return response;
}
function unableToResolve() {
return new Response('<h1>Service Unavailable</h1>', {
status: 503,
statusText: 'Service Unavailable',
headers: new Headers({
'Content-Type': 'text/html'
})
});
}
})
);
});
self.addEventListener("activate", function (event) {
event.waitUntil(
caches
.keys()
.then(function (keys) {
return Promise.all(
keys
.filter(function (key) {
return !key.startsWith(version);
})
.map(function (key) {
return caches.delete(key);
})
);
})
);
});
I don't see how you are setting the version, but I presume multiple caches still exist (I can see you are trying to delete the previous caches but still). caches.match() is a convenience method and the order is not guaranteed (at least Chrome seems to query the oldest one first). Chrome Developer Tools shows you the existing caches (Application/Cache/Cache Storage) and their contents. If you want to query a specific cache, you'll need to do:
caches.open(currentCacheName).then(function(cache) {...}
as in the example in the Cache documentation.
I am using ionic Framework. i have multiple HTTP service which is working fine. Now problem is that whenever i get response of any http call. i can't proceed further.
Can we run HTTP Service as a background process. So my application continues works without waiting for result.
here is my code
articleService.getArticles().then(function() {
},function(err){
});
and sercvice code
$http({
url: "http://myservice.com",
data: { user_id: 1 },
method: 'POST',
withCredentials: true,
}).success(function (data) {
deferred.resolve(data);
}).error(function (err) {
deferred.resolve(0);
})
return deferred.promise;
}
Any idea? I need a solution in ionic framework which will work both for ios and andriod?
Thanks
try to use html5 web workers what u need to do is multithreading and because that javascript is single threading environment you have to web workers
https://html.spec.whatwg.org/multipage/workers.html
Look at this plunker this what you need and it is all angularjs so will work with ionic.
var app = angular.module('angularjs-starter', []);
app.config(function($routeProvider) {
$routeProvider.
when('/', {controller:'StartCtrl', templateUrl:'start.html'}).
when('/main', {controller:'MainCtrl', templateUrl:'main.html'}).
otherwise({redirectTo:'/'});
});
app.controller('MainCtrl', function($scope, Poller) {
$scope.name = 'World';
$scope.data = Poller.data;
});
app.controller('StartCtrl',function(){});
app.run(function(Poller) {});
app.factory('Poller', function($http, $timeout) {
var data = { response: {}, calls: 0 };
var poller = function() {
$http.get('data.json').then(function(r) {
data.response = r.data;
data.calls++;
$timeout(poller, 1000);
});
};
poller();
return {
data: data
};
});
Maybe i misunderstand your question but i think your service code is wrong.
Try something like this
myApp.factory('articleService', function($http) {
return {
getArticles: function getArticles() {
return $http({...}); // $http returns a promise, so you dont need your own defer.promise
}
}
});
//usage
//first: send or get data async
articleService.getArticles().then(function(resp){
alert('called second');
...
});
// second: do something else, this will not wait for your response
alert('called first');
I've looked at multiple solutions to this problem but nothing's working to fix my problem.
I'm using ASP.NET MVC 4.5.
Here are my steps:
Use ajax call in page to upload file.
Within same function that generates ajax call run an ajax call to refresh the page to include the uploaded file, after ajax call is finished.
I'm using this as the first call (to upload) (compliments of another Stack Overflow user):
function uploadFiles() {
document.getElementById('fileupload').onsubmit = function () {
var formdata = new FormData(); //FormData object
var fileInput = document.getElementById('uploadfilenames');
//Iterating through each files selected in fileInput
for (i = 0; i < fileInput.files.length; i++) {
//Appending each file to FormData object
formdata.append(fileInput.files[i].name, fileInput.files[i]);
}
//Creating an XMLHttpRequest and sending
var xhr = new XMLHttpRequest();
xhr.open('POST', '/Dashboard/UploadFiles');
xhr.send(formdata);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
//alert(xhr.responseText);
}
}
return false;
}
reloadMain();
}
The reloadMain() function is:
function reloadMain() {
$.ajax({
url: '/Dashboard/ThumbList/' + currentPath,
type: "GET",
timeout: 5000,
success: function (msg) {
$("#thumb-list").html(msg)
},
error: displayError("Unable to get file listing")
});
}
I have noticed this:
The 'refresh' doesn't include the uploaded file information in the response
IE11 and Chrome act differently.
It seems that the problem is that the controller/system doesn't complete the file operations soon enough (I saw a "denied access...file in use" error when using Chrome.
So, it would seem that the refresh ajax call needs to wait until the file system completes its work.
Would you agree? If so, how can I make this work?
You can either set your XMLHttpRequest async to false:
xhr.open('POST', '/Dashboard/UploadFiles', false);
Or you can call your refresh function in callback:
var xhr = new XMLHttpRequest();
xhr.open('POST', '/Dashboard/UploadFiles');
xhr.send(formdata);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
reloadMain(); //Only refresh after the file post get a 200 response
}
}