AngularJS + Android 4.2: very slow to react - javascript

Our web app is built with AngularJS v1.2.26 We're troubleshooting an issue that only seems to happen on older Android browsers, specifically 4.2...
In short, asynchronous things are happening ~600% slower than they should. For example, we show an error message in response to a failed HTTP request. The service in charge of making the request has a variable which holds status messages. Then a controller watches it like this:
// Status msg ctrl
var statusCtrl = app.controller('statusCtrl', function($scope, updateService, $timeout) {
$scope.message = false;
//watch for status messages
$scope.$watch(function () { return updateService.loadingTroubleMsg; },
function (value) {
$scope.message = value;
}
);
});
In other browsers this works perfectly. The http request fails and then the message appears right away. On the Android browser (which the client is running from a USB) the message shows up ~20 minutes later.
I have a couple theories:
The promise is taking a very long time to be resolved,
The $watch is happing in slow motion.
The http request is taking a reallllly long time to timeout.
There are no visible errors, and everything else seems to work. Unfortunately, I do not have the exact USB Android device in my position, which makes troubleshooting more difficult.
Why would this happen? Are any of my theories more plausible then the others? Any advice on how to get to the bottom of this is welcome.

It turned out to be #3. I added a "timeout" limit to the $http config settings and it started responding normally. I don't know why this only mattered on Android 4.2. I'm guessing it's related to its lack of (native) support for promises.

Related

self.skipWaiting() not working in Service Worker

I have a service worker. Here's the install event:
self.addEventListener('install', function (event) {
console.log('Installing Service Worker ...', event);
return self.skipWaiting()
.then(() => caches.open(CACHE_STATIC_NAME))
.then(function (cache) {
return cache.addAll([
'./file1.html',
'./file2.html'
])
})
});
For some reason, when I edit the service worker code and update the query parameter in the service worker file URL, it installs but does not activate (according to Chrome DevTools) — even though I've called self.skipWaiting().
Oddly if I go into the console, go to the scope of the service worker and type self.skipWaiting() myself, it activates immediately.
I've been trying to work out what's going on for many hours now, and I'm completely stumped. Is there something I'm missing here?
The old SW might not stop while it's still running tasks - for example if it had a long running fetch request (e.g server sent events / event source, or fetch streams; although I don't think websockets can cause this as SW will ignore them I think).
I find the behaviour to be different between browers however. Chrome seems to wait while the existing task is running (so skipWaiting will fail...), but Safari seems to kill the task and activate the new SW.
A good way to test if this is causing your issue would be to kill your server just after you request the skipWaiting (to kill the network connections). (Just clicking "Offline" in Dev Tools doesn't seem to kill all running connections, for example EventSources stay running.)
You can have the SW ignore certain routes (below), or you could try and force the requests to terminate (maybe using AbortController).
self.addEventListener('fetch', function(event) {
const { method, url } = event.request;
if(event.request.method !== "GET") return false;
if(url === "https://example.com/poll") return false;
event.respondWith(
caches.match(match).then(function(response) {
return response || fetch(event.request);
})
);
});
The process for skipWaiting is in this spec:
https://w3c.github.io/ServiceWorker/#try-activate-algorithm
But I don't find it very clear about whether the browser should wait for tasks or terminate them (or transfer them to the new SW?), before activating the new SW; and as mentioned, it seems to work differently between browsers at the moment...
I had experienced the same issue.
I validated my issue by the trick given by #ej.daly that I should stop the server & the WAITING service-worker will become active in few minutes.
After that, I applied a hack in my code: Reload the window if the WAITING service worker isn't activated in next 3 seconds.
BTW, I am developing a library to make service-worker installation easy for the various (commonly used) scenarios in our company. Have a look at https://github.com/DreamworldSolutions/workbox-installer

SignalR freezes at 'Initiating start request'

I am experiencing the strangest behavior with SignalR. After some refactoring I encountered issues with connectivity. I think it was pure luck that made it work before as the code didn't comply to the recommended practices. For instance I started the hub before I declared the hub methods (as explained by David Fowler) so the client never explicitly subscribed to those hubs but somehow they did. I have looked all day why and how but no luck.
After refactoring this is the code that is executed once the document is loaded:
function initSignalR() {
var me = this,
appointmentHub = $.connection.appointmentHub,
assignmentHub = $.connection.assignmentHub,
taskHub = $.connection.taskHub,
notificationHub = $.connection.notificationHub,
messageHub = $.connection.messageHub;
$.connection.hub.connectionSlow(onConnectionSlow);
$.connection.hub.stateChanged(onStateChanged);
$.connection.hub.error(onError);
$.connection.hub.disconnected(onDisconnected);
$.connection.hub.logging = true;
appointmentHub.client.updateAppointment = onUpdateAppointment;
$.connection.hub.start().done(onDone).fail(onFailed);
... code ommitted for brevity ...
}
function onUpdatedAppointment(appointment) {
.... code ommitted for brevity ....
}
These are the logs that appear in the console when it occassionally works:
Client subscribed to hub 'appointmenthub'
Negotiating with '/signalr/negotiate?clientProtocol=1.5&connectionData=%5B%7B%22name%22%3A%22appointmenthub%22%7D%5D'
webSockets transport starting.
Connecting to websocket endpoint 'ws://localhost:52541/signalr/connect?....
Websocket opened.
webSockets transport connected. Initiating start request.
The start request succeeded. Transitioning to the connected state.
Now monitoring keep alive with a warning timeout of 13333.333333333332, keep alive timeout of 20000 and disconnecting timeout of 30000
I wrote 'occassionally' on purpose because every now and then SignalR connects correctly. Sadly however most of the time I don't get that far. Usually the last visible step in the console is :
webSockets transport connected. Initiating start request.
For some time I thought it was the body of the callback method that caused the issue I could connect more when I had an empty function but even that isn't the cause. So I'm running out of ideas what to do next.
For the sake of completeness, here is the startup code in ASP.NET MVC:
GlobalHost.Configuration.TransportConnectTimeout = TimeSpan.FromSeconds(50);
GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(110);
GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule());
GlobalHost.DependencyResolver.Register(typeof(IJavaScriptMinifier), () => new SignalRMinifier());
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => new UserIdProvider());
HubConfiguration hubConfiguration = new HubConfiguration()
{
EnableDetailedErrors = true,
};
return app.MapSignalR(hubConfiguration);
I found a similar issue but no solution has been provided there.
I tested this in IE/Edge/Chrome/Firefox/Opera and all have the same results. The application is running on ASP.NET MVC5 and the latest version of SignalR (2.2.1) is used.
So it turns out there was nothing wrong with the client side code or the configuration of SignalR, but the problem was situated in the Hub class.
I had some custom code in the OnConnected method which was causing timeouts and/or errors (connections to external services and databases were made there). By dispatching the work to a different process (e.g. using Hangfire or NServiceBus) I was able to fix the issue as mentioned before.
In hindsight the described behavior makes total sense. The lesson I learned here is to treat Hubs like controllers in ASP.NET MVC: they should be fat free and should contain limited business logic.

How to use $interval for continuous polling in Angular?

In my Angular code, I have a code for long polling, that looks like this
var request = function() {
$http.post(url).then(function(res) {
var shouldStop = handleData(res);
if (!shouldStop()) {
request()
}
};
}
request();
The function gets called immediately after the page load.
However, now I am trying to set up testing in Protractor and I got this error message
Failed: Timed out waiting for Protractor to synchronize with the page after 11 seconds. Please see
https://github.com/angular/protractor/blob/master/docs/faq.md. The following tasks were pending:
In the docs, I read the following:
Before performing any action, Protractor asks Angular to wait until the page is synchronized. This means that all timeouts and http requests are finished. If your application continuously polls $timeout or $http, it will never be registered as completely loaded. You should use the $interval service (interval.js) for anything that polls continuously (introduced in Angular 1.2rc3).
How should I edit my code to use $interval? I thought that interval is an angular wrapper for window.setInterval, I am not sure how to use that for long polling.
Oh, the $interval thing in the docs belongs to the $timeout, not to the $http.
Well, I will just throw away Angular's $http and will just use fetch (with additional $rootScope.$apply and JSON deserialization) to do the same thing

Handling HTTP 401 with WWW-Authenticate in Cordova / Ionic iOS application

I'm currently working on a mobile app built on Cordova and Ionic. I am dealing with a third-party API (i.e. it cannot, and will not be changed for this app to work).
When a user of the app is unauthenticated - be that if their session has expired or otherwise - the API responds with an HTTP 401, with a WWW-Authenticate header.
In a browser while developing this is fine, but while on an iPhone, or in a simulator it does not appear, and the app has to reach the timeout period for the request. When that timeout is reached, the request is cancelled. This means that in the JavaScript, we simply get back a HTTP status of 0, with no real data to identify whether or not there was a timeout, or an authentication issue.
Currently, I've put in place some educated guesswork like checking if the phone has connectivity when a timeout occurs etc, but this is not an ideal solution as the user still has to wait for that timeout, and it's not always correct.
How can I check when the HTTP 401 dialog has appeared and is expecting a response? I need to be able to identify when an actual 401 occurs, and when a request simply times out.
If there is a method in JavaScript to accomplish then, then that'd be great. A native solution would also work, be it a plugin or otherwise.
I am dealing with the same issue at the moment.
The issue is that when a 401 is being returned, it has a WWW-Authenticate piece, which tells the browser to pop up that little popup for you to try again. This isn't handled "right" (depends on who you ask I guess) in the iOS web container, which results in Cordova never to see the request.
The cordova bug is logged here: https://issues.apache.org/jira/browse/CB-2415
I don't understand how such a major issue hasn't been resolved, yet. But I am sure there are some technicalities around it. If you check out the updates, you see that Tobias Bocanegra ( https://github.com/tripodsan/cordova-ios/commit/5f0133c026d6e21c93ab1ca0e146e125dfbe8f7e ) added a "quick hack" to solve the problem. Maybe that helps you further. It didn't help me in my situation.
What I did in my case for a temporary fix:
I passed the http requests into the loading modal, which has a cancel button. So, it is up to the user to cancel or just wait. It's horrible, but it worked in my case. (Internal app, etc.)
Well I am not sure why the third-party API woudn't send you normal HTTP error codes. If you are connecting to the API with the use of $http you can add response interceptors to it, for example read next article:
http://codingsmackdown.tv/blog/2013/01/02/using-response-interceptors-to-show-and-hide-a-loading-widget/
Within the next error handler code you can add some code to evaluate the HTTP status code:
function error(response) {
// Added code for specific HTTP error codes
if (response.status === 401) {
console.log('Received 401');
}
// get $http via $injector because of circular dependency problem
$http = $http || $injector.get('$http');
if($http.pendingRequests.length < 1) {
$('#loadingWidget').hide();
}
return $q.reject(response);
}
Also see the AngularJS documentation about interceptors.
A native solution would also work, be it a plugin or otherwise.
I had the same problem and I could fix it with the cordova http plugin. Just install it via ionic plugin add cordova-plugin-advanced-http (check documentation here). Then your xhttp calls will be done natively and not out of the webView. Then responses with a ´WWW-Authenticate´ headers will not timeout anymore and you can properly handle a 401
You can use it in your code like this:
try { // important since it will not work in dev mode in your browser (e.g. chrome)
if (cordova && cordova.plugin && cordova.plugin.http) {
var server_url = 'https://example.com';
var your_endpoind = '/auth-me-example';
cordova.plugin.http.useBasicAuth('some_user', 'and_password');
cordova.plugin.http.setHeader(server_url, 'Content-type', 'application/json');
cordova.plugin.http.get(server_url + your_endpoind, {}, {}, function (response) {
console.log('response: ', JSON.stringify(response));
}, function (response) {
console.error('error response: ', JSON.stringify(response));
});
}
} catch (e) {
console.error('could not make native HTTP request!');
}
PS: if you are using angular2+ npm install --save #ionic-native/http is also quite useful.

SignalR & IE Issue - poll is pending

I have a Problem With IE and SignalR, I'm using the it to perform a Syncing action between two databases, the Actions Completed successfully on Google Chrome / Firefox / Safari in all scenarios.
Using IE for the First time the sync performed successfully but only for one time, in the second time a pending request stack and the page stay freeze for ever.
I found a solution online which is changing the transport mode.
But page still freezing.
if (isIE()) {
$.connection.hub.start({ transport: ['serverSentEvents','foreverFrame']}).done(function () {
progressNotifier.server.DoMyLongAction();
});
}else{
$.connection.hub.start({ transport: ['serverSentEvents','longPolling'] }).done(function () {
progressNotifier.server.DoMyLongAction();
});
}
I'm Using:
SgnalR v2.1.0.0
.Net framework v4.5
jquery v1.8
is it an Issue or I'm Doing something wrong ?
Edit
my application use Jquery progress bar and i Update this progress bar using this Code:
server side:
Clients.Caller.sendMessage(msg, 5, "Accounts");
client side:
progressNotifier.client.sendMessage = function (message, value, Entity) {
pbar1.progressbar("value", nvalue);
};
it's working on Firefox so I thought it's a signalR Issue !! Now i became confused if it's working as expected then what causes this problem ?
you can try use EventSource (SSE).
I am using this:
https://github.com/remy/polyfills/blob/master/EventSource.js
but modified, for SignalR:
http://a7.org/scripts/jquery/eventsource_edited.js
I am working with it for one year, SignalR just check for window.EventSource and it works.
The solution you found online is not likely to help your issue.
I doubt your IsIE() function is correctly identifying IE. If it was, SignalR should only be attempting to establish a "foreverFrame" connection, since IE does not even support "serverSentEvents". I would not expect IE to make any "/signalr/poll" requests, because those requests are only made by the "longPolling" transport.
Also, having a "pending" poll request in the IE F12 tool's network tab is entirely expected. This is how long polling is designed to work. Basically, as soon as a message is received the client makes a new ajax request (a long poll) to retrieve new messages. If no new messages are immediately available, the server will wait (for up to 110 seconds by default in the case of SignalR, not forever) for a new message to be sent to the client before responding to the pending long poll request with the new message.
Can you clarify exactly what issue you are having other than seeing a pending poll request showing up under the network tab? It would also help if you you enabled tracing in the JS client, provided the console output, and showed all the "/signalr/..." requests in the network tab.

Categories

Resources