AJAX Submitted in order, starts in order, received out of order - ajax

I have a web application that submits an ajax request (to start an IR LED sending data) onmousedown, and stops it onmouseup. Generally works.
However if one is clicking fast I get the stop command going to the LED control before the start command. I added code to track order and the actual event submission (open on XMLHttpRequest) is in the proper order (and they are async). Then I instrumented the onstatechange to log each state and a time stamp when that state change occurs, and what I see is occasionally while the started (0) and server connection initialized (1) states occur in the proper order, the "received" (2) and subsequent states occur out of order.
My first question is whether these events are SUPPOSED to occur in the order submitted?
And if so, any idea what could cause them not to?
And if processing order is not assured, is there any way to force it (short of building my own queue and allowing only one ajax call to be outstanding, which is a paint)?
To demonstrate the issue (with client on Windows 8.1 and Chrome 39.0.2171.71m as well as IE 11.0.14, server on a raspberry pi running php 5.4.4-14+deb7u11 and Apache/2.2.22), here's a log captured from a series of events. It's small and a bit obscure, so I tried to color code each click (green, blue, orange in sequence), and the state code is in the 3rd column. Notice up (highlighted in yellow) precedes the down in the 3rd click once it gets to state 2.
Next is the code that submits it. I believe a lot of it to be irrelevant to the problem but am including it just in case. Note "RemoteFunc" is called both on mouseup and mousedown as well as (for separate objects) plain click, but each with a different func, then a POST is done. Each object either uses mouseup/mousedown or onclick, never both.
The processing time for the post can easily be greater then the duration of a mouse click, so it is not unexpected for a new "down" to come before the first "down" or "up" completes, but the issue is that I want them to process in order. And indeed they seem to be submitting in order and starting (going to state 1) in order, but are received out of order, so I assume ajax is creating multiple connections, and the response to those connections is getting out of order.
Which makes me suspect that order is not guaranteed. But I haven't found anything to confirm that (other than absence of a guarantee to be in order).
Any easy solution other than to manually build a queue and single stream the requests?
function RemoteFunc(str,func)
{
var xmlhttp=new XMLHttpRequest();
if(event.button!=0)
{
AddMessageEntry(str + " dismissed - not left click",true);
return true; //Only respond to left mouse down so context menus do not fire click and cause a competing event
}
if(taskrunning!="")
{
AddMessageEntry(str + " ignored, " + taskrunning + " already running",false);
return true;
}
if(func=="remoteup.cgi") { updown=" mouse-up"; }
else if (func=="remotedown.cgi") { updown=" mouse-down"; }
else { updown=""; }
AddMessageEntry(str + updown + " Started",true);
xmlhttp.onreadystatechange=function()
{
// if (xmlhttp.readyState==4)
// {
if(func=="remoteup.cgi") { updown=" mouse-up"; } // because this can run in overlapping functions we need to recalculate this here
else if (func="remotedown.cgi") { updown=" mouse-down"; }
else { updown=""; }
AddMessageEntry(str + updown + " " + states[xmlhttp.readyState] + " " + xmlhttp.responseText,false);
if(xmlhttp.readyState==4) {taskrunning="";}
// }
}
if(event.type.toString()=="click") // We only need to track running tasks invoked with a click -- mousedown/up are async since they run continually (from down) until stopped (from up)
{
taskrunning=str;
}
xmlhttp.open("POST","cgi-bin/" + func + "?Button="+str,true);
xmlhttp.send();
}

Related

AJAX and Leaflet: Inspect feature properties before styling/adding to map

I'm using leaflet-ajax to load geoJSON on demand. I want to find the maximum theProperty value so I can use that to scale the feature's fill colors before I add them to the map.
Here's my general approach:
function maxBinPropertyValue(theProperty) {
var theGeoJson = null;
var maxPropertyValue = 0;
var propertyValue = null;
var theGeoJson = new L.GeoJSON.AJAX(binsFileName());
theGeoJson.on('data:loaded', function() {
console.log('The data is loaded');
theGeoJson.eachLayer(function(layer) {
console.log('Looping through the layers');
propertyValue = feature.properties[theProperty];
if (propertyValue > maxPropertyValue) {
maxPropertyValue = propertyValue;
console.log('Max so far: ' + maxPropertyValue);
};
});
});
theGeoJson = null;
console.log('The final maximum value: ' + maxPropertyValue);
return maxPropertyValue;
};
I'm trying to wait for the data:loaded event, then loop through all the features to find the maximum value of theProperty, which is returned to the calling routine.
Except it doesn't work. The first console.log says 'The data is loaded'. The second and third console.logs are never reached, and the fourth and final one reports a value of 0 for maxPropertyValue.
How can I examine all the features in a featureset before styling them, in a way guaranteed to not have asynchronous problems?
PS: I'm pretty sure I can't use onEachFeature: instead of the above approach, because I need to examine every feature's property to determine the maximum value in the set before I can style any of the features.
As for your issue about inspecting your data and retrieving the maximum value, you are indeed facing the classic asynchronous concept of JavaScript.
See How do I return the response from an asynchronous call?
Asynchronism is a problem if not dealt with properly, but an advantage if correctly handled.
To put the concept shortly, you do not manage asynchronism in a "standard" sequential way, but you should rather consider parts of code (callbacks) that are executed at a later time based on events.
Whenever you provide a function as an argument, it is certainly a callback that will be executed at a later time, but very probably much later than the next instructions.
So in your case, your 2nd and 3rd console.log are within a callback, and will be executed once your data is loaded, which will happen much later than your 4th console.log.
As for your next step (styling and adding to map), you actually do not need to perform an extra AJAX call, since you already have all data available in theGeoJson variable. You simply need to refactor / restyle it properly.
It is a good approach to break your problem in small steps indeed.
Good luck!
PS: that being said, ES7 provides async and await functionalities that will emulate a sequential execution for asynchronous functions. But to be able to use those, you need latest browser versions or transpilation, which is probably more work to learn and configure as of today for a beginner than understanding how to work with async JS.
I also had this problem and had to wrap my head around this, so giving an explicit example for solution here;
// make a request with your "url"
var geojsonLayer = new L.GeoJSON.AJAX("url");
// define your functions to interact with data
function thingToDoBeforeLoadingStarts () {
// do stuff
}
function thingToDoForEachFileDownloaded () {
// do stuff
}
function thingToDoAfterAllDownloadEnds () {
// do stuff
}
// attach listeners
geojsonlayer.on("data:loading",thingToDoBeforeLoadingStarts);
geojsonLayer.on("data:progress",thingToDoForEachFileDownloaded)
geojsonLayer.on("data:loaded",thingToDoAfterAllDownloadEnds);

How to keep UI responsive when consuming items produced by background thread producer?

I've offloaded a long-running, synchronous, operation to a background thread. It takes a while to get going, but eventually it starts producing items very nicely.
The question is then how to consume then - while maintaining a responsive UI (i.e. responding to Paint and UserInput messages).
One lock-free example sets up a while loop; we consume items while they are items to consume:
// You call this function when the consumer receives the
// signal raised by WakeConsumer().
void ConsumeWork()
{
Thing item;
while ((item = InterlockedGetItemOffTheSharedList(sharedList)) != nil)
{
ConsumeTheThing(item);
}
}
The problem is that the background thread, once it gets going, can produce the items very quickly. This means that my while loop will never have a chance to stop. That means it will never go back to the message queue to respond to pending paint and mouse input events.
I've turned my asynchronous multi-threaded application in a synchronous wait as it sits inside:
while (StuffToDo)
{
Consume(item);
}
Posting Messages
Another idea is to have the background thread PostMessage a message to the main thread every time an item is available:
ProduceItemsThreadMethod()
{
Preamble();
while (StuffToProduce())
{
Thing item = new Item();
SetupTheItem(item);
InterlockedAddItemToTheSharedList(item);
PostMessage(hwndMainThreadListener, WM_ItemReady, 0, 0);
}
}
The problem with this is that any posted message is always higher priority than any:
paint messages
mouse move messages
So as long as there is posted messages available, my application will not be responding to paint and input messages.
while GetMessage(out msg)
{
DispatchMessage(out msg);
}
Every call to GetMessage will return a fresh WM_ItemReady message. My Windows message processing will be flooded with ItemReady messages - preventing me from processing paints until all the items have been added.
I've turned my asynchronous multi-threaded application in a synchronous wait.
Limiting the number of posted messages doesn't help
The above is actually worse than the first variation, because we flood the main thread with posted messages. What we want to do is only post a message if the main thread hasn't dealt with the previous message we posted. We can create a flag that is used to indicate if we've already posted a message, and if the main thread still hasn't processed it
ProduceItemsThreadMethod()
{
Preamble();
while (StuffToProduce())
{
Thing item = new Item();
SetupTheItem(item);
InterlockedAddItemToTheSharedList(item);
//Only post a message if the main thread has a message waiting
int oldFlagValue = Interlocked.Exchange(g_ItemsReady, 1);
if (oldFlagValue == 0)
PostMessage(hwndMainThreadListener, WM_ItemReady, 0, 0);
}
}
And in the main thread we clear the "ItemsReady" flag when we've processed the queued items:
void ConsumeWork()
{
Thing item;
while ((item = InterlockedGetItemOffTheSharedList(sharedList)) != nil)
{
ConsumeTheThing(item);
}
Interlocked.Exchange(g_ItemsReady, 0); //tell the thread it can post messages to us again
}
The problem again is that the thread can fill the list faster than we can consume it; so we never get a change to fall out of the ConsumeWork() function in order to handle user input.
As soon as ConsumeWork returns, the background producer thread generates a new WM_ItemReady message. The very next time i call GetMessage
while GetMessage(out msg)
{
DispatchMessage(out msg);
}
it will be a WM_itemReady message. I will be stuck in a loop.
I've turned my asynchronous multi-threaded application in a synchronous wait.
Limiting ourselves to a count of items doesn't help
We could try forcing a break out of the while loop after, say, processing 100 items:
void ConsumeWork()
{
int itemsProcessed = 0;
Thing item;
while ((item = InterlockedGetItemOffTheSharedList(sharedList)) != nil)
{
ConsumeTheThing(item);
itemsProcessed += 1;
if (itemsProcessed >= 250)
break;
}
Interlocked.Exchange(g_ItemsReady, 0); //tell the thread it can post messages to us again
}
This suffers from the same problem as the previous incarnation. Although we will leave the while loop, the very next message we will recieve will again be the WM_ItemReady:
while (GetMessage(...) != 0)
{
TranslateMessge(...);
DispatchMessage(...);
}
that's because WM_PAINT messages will only appear if there are no other messages. And the thread is itching to create a new WM_ItemReady message and post it in my queue.
Pumping the message loop myself?
Some people cry a little inside when they see people manually pumping messages to fix unresponsive applications. So lets try manually pumping messages to fix unresponsive applications!
void ConsumeWork()
{
Thing item;
while ((item = InterlockedGetItemOffTheSharedList(sharedList)) != nil)
{
ConsumeTheThing(item);
ManuallyPumpPaintAndInputEvents();
}
Interlocked.Exchange(g_ItemsReady, 0); //tell the thread it can post messages to us again
}
I won't go into the details of that function, because it leads to the re-entrancy problem. If the user of my library happens to try to close the window they're on, destroying my helper class with it, i will suddenly come back to execution inside a class that has been destroyed:
ConsumeTheThing(item);
ManuallyPumpPaintAndInputEvents(); //processes WM_LBUTTONDOWN messages will closes the window which destroys me
InterlockedGetItemOffTheSharedList(sharedList) //sharedList no longer exist BOOM
Down and down I go
I keep going in circles trying to solve the problem of how to maintain a responsive UI when using background threads. I've tinkered with four solutions in this question, and three others before asking it.
I can't be the first person to have used the Producer-Consumer model in a user interface.
How do you maintain a responsive UI?
If only i could post a message with priority lower than Paint, Input, and Timer :(

Double tap recognition takes too long? (Hammer.js 2.0)

I am programming a highly responsive web application and came across the issue that most time is used to recognize double taps.
I am using this code from the website:
var singleTap = new Hammer.Tap({ event: 'singletap' });
var doubleTap = new Hammer.Tap({event: 'doubletap', taps: 2 });
hammer.add([doubleTap, singleTap]);
doubleTap.recognizeWith(singleTap);
singleTap.requireFailure(doubleTap);
This basically works quite fine. However, due to the timeouts/intervals the recognition of a double tap takes quite "long". I guess its about 2 times the interval - one for each tap.
The waiting for the last interval (waiting for a third tap) is senseless in my scenario.
Is there any "ok tapCount == 2, we fire now and don't wait any longer"-TapRecognizer option?
Update, I have done some logging:
First column: passed ms since first event
0 input: mousedown
74ms input: mouseup
145ms input: mousedown
218ms input: mouseup
520ms double tap
-
0 input: mousedown
64ms input: mouseup
366ms single tap
This confirms my theory that double tap is waiting for a third click but I don't think there's an option to disable this.
I share my solution to the problem:
I copied the TapRecognizer and named it DblTapRecognizer. The interesting source code lines are:
if (tapCount === 0) {
// no failing requirements, immediately trigger the tap event
// or wait as long as the multitap interval to trigger
if (!this.hasRequireFailures()) {
return STATE_RECOGNIZED;
} else {
this._timer = setTimeoutContext(function() {
this.state = STATE_RECOGNIZED;
this.tryEmit();
}, options.interval, this);
return STATE_BEGAN;
}
}
"if (!this.hasRequireFailures())" seems to misbehave in my situation, since the comment hints at immediate firing... So just "return STATE_RECOGNIZED;" and delete the rest for the DblTapRecognizer.
We ran into similar slowness issues. Apparently there is an inherent lag on tap action on touch devices.
We ended up using FastClick
All you need to do is FastClick.attach(document.body);
This improved the "tap performance" for us.

ajaxStop fires twice... sometimes

Based on the issue in this question (ajaxStop was firing twice), I wrote the following ajaxStop event.
var ajaxCount = 0;
$(document).ajaxStop(function () {
ajaxCount += 1;
console.debug('calling ajaxStop, iteration ' + ajaxCount);
if (ajaxCount == 2) {
$('.fieldLoading').hide();
$('.fieldValue').show();
}
});
9 times out of 10 it works exactly as expected. The debug console shows "calling ajaxStop, iteration 1" as soon as the page loads. Then, after everything else fires, it shows "calling ajaxStop, iteration 2". This is what I expect. However, about 5 or 10 percent of the time it only displays iteration 1, after everything has fired (which means no data is shown).
I suggest adding an ajaxSend() handler to count the number of ajax requests and converting the ajaxStop() to an ajaxComplete(). Rather than performing:
if (ajaxCount == 2)
You can then do:
if (ajaxStopCount == ajaxStartCount)
Additionally, you could modify your counters to count ACTIVE requests (decrement the counter on ajaxComplete, increment it on ajaxSend (your loading dialog might disappear between requests, but will re-appear as soon as another request begins; I wouldn't image much of a delay between hiding/showing, but that depends on your code organization).
Add another handler for errors, and you should be set.
I ended up using a queue like so:
var ajaxQueue = $({});
$.ajaxQueue = function (ajaxOpts) {
// Hold the original complete function.
var oldComplete = ajaxOpts.complete;
// Queue our ajax request.
ajaxQueue.queue(function (next) {
// Create a complete callback to fire the next event in the queue.
ajaxOpts.complete = function () {
// Fire the original complete if it was there.
if (oldComplete) {
oldComplete.apply(this, arguments);
}
// Run the next query in the queue.
next();
};
// Run the query.
$.ajax(ajaxOpts);
});
};
Now I just call all my ajax events as ajaxQueue() instead of ajax(). Then, I have a $(document).ajaxStop() that I use to finish everything up.

Is there a way to break out of event recursion in jQuery without using a global variable?

I have a form with 2 text inputs and 2 span controls. Normally, when textbox A is changed an event is fired to change span A, and when textbox B is changed, an event is fired to change span B.
However, in one particualar case I would like a change either textbox A or textbox B to update both span A and B. I tried wiring the events up to the corresponding controls in this case, but it didn't work because there is much state that is set up in the event building code (not to mention each event calls 'this', which would make the logic use the wrong control if it were fired from a different one than it was intended).
To make things easy, it would be best to pass a string (representing the other text input id) to the event handler at the time it is created, and then calling the change() event manually on the second control. However, this puts things in an infinite loop of recursion. I thought of a way to get around the recursion, but it reqires a global variable.
Is there a better way than this, preferably one that doesn't require a global variable?
ml_inEvent = false;
$ctlToVal.bind('keyup change', {feedbackCtl: ml_feedback, controlsToMonitor: ary, validationGroup: this.validationGroup, controlToFire: ctlToFire}, function(event) {
// Other processing happens here...
if (event.data.controlToFire != '') {
var $controlToFire = $('#' + event.data.controlToFire);
if ($controlToFire.length) {
// Use a global variable to ensure this event is not fired again
// as a result of calling the other one
if (!ml_inEvent) {
ml_inEvent = true;
$controlToFire.change();
ml_inEvent = false;
}
}
}
});
You can use the extraParameters argument on .trigger() to break out, for example:
$ctlToVal.bind('keyup change', {feedbackCtl: ml_feedback, controlsToMonitor: ary, validationGroup: this.validationGroup, controlToFire: ctlToFire}, function(event, fire) {
// Other processing happens here...
if(fire !== false) $('#' + event.data.controlToFire).trigger('change', false);
});
You can give it a try here. What this does is the event handler callback not only receives the event object but also any other arguments you pass in, in this case we're just using a simple false and a !=== check this in important so undefined (the parameter not passed at all) still changes both controls. So for instance $("#control").change() would change both controls, but still not loop...you can test that here.

Resources