DOM input events vs. setTimeout/setInterval order?

If so instead of using timeouts or intervals you might want to use Web Workers instead. All modern browsers including IE9 should support Web Workers. I through together an example page (couldn't put it on jsfiddle since Web Workers rely on an external .

Js file that has to be hosted on the same origin). If you click A, B, C or D a message will be logged on the right. When you press start a Web Worker starts processing for 3 seconds.

Any clicks during those 3 seconds will be immediately logged. The important parts of the code are here: func1. Js The code that runs inside the Web Worker onmessage = function (e) { var result, data = e.

Data, // get the data passed in when this worker was called // data now contains the JS literal {theData: 'to be processed by func1'} startTime; // wait for a second startTime = (new Date).getTime(); while ((new Date).getTime() - startTime Js"); // this is the callback which will fire when "func1. Js" is done executing worker. Onmessage = function(event) { log('Func1 finished'); func2(); }; worker.

Onerror = function(error) { throw error; }; // send some data to be processed log('Firing Func1'); worker. PostMessage({theData: 'to be processed by func1'}).

Web Workers are a good suggestion, but 1) not universally supported (e.g. They're nowhere to be found in IE.

I think you are on the wrong track with your experiments. One problem is of course that you are fighting different message loop implementations here. The other (the one you didn't recognize it seems) is different double click handling.

If you click the link twice you won't get two click events in MSIE - it's rather one click event and a dblclick event (for you that looks like the second click was "swallowed"). All other browsers seem to generate two click events and a dblclick event in this scenario. So you need to handle dblclick events as well.As message loops go, Firefox should be easiest to handle.

From all I know, Firefox adds messages to the queue even when JavaScript code is running. So a simple setTimeout(..., 0) is sufficient to run code after the messages are processed. You should refrain from hiding the link after func1() is done however - at this point clicks aren't processed yet and they won't trigger event handlers on a hidden element.

Note that even a zero timeout doesn't get added to the queue immediately, current Firefox versions have 4 milliseconds as the lowest possible timeout value. MSIE is similar, only that there you need to handle dblclick events as I mentioned before. Opera seems to work like that as well but it doesn't like it if you don't call event.preventDefault() (or return false from the event handler which is essentially the same thing).

Chrome however seems to add the timeout to the queue first and only add incoming messages after that. Nesting two timeouts (with zero timeout value) seems to do the job here. The only browser where I cannot make things work reliably is Safari (version 4.0 on Windows).

The scheduling of messages seems random there, looks like timers there execute on a different thread and can push messages into the message queue at random times. In the end you probably have to accept that your code might not get interrupted on the first occasion and the user might have to wait a second longer. Here is my adaptation of your code: http://jsfiddle.net/KBFqn/7.

On Firefox 5 for Mac, no matter how many times I click during the loop in your example, I get 0-2 interrupts. So I'm afraid I can't accept this as a solution. The point about double-clicking is a good one, though.

Perhaps we could clarify matters by testing with keyboard events instead? – Trevor Burnham Jun 23 at 19:32 @Trevor Burnham: I see, message queue handling in Firefox on Mac must be different then (all the testing above happened on Windows). Too bad.

– Wladimir Palant Jun 23 at 19:39 @Trevor Burnham: I switched to keypress handling in jsfiddle. Net/KBFqn/8 and it seems to work correctly for me in all browsers, even in Safari. Click the link and then press some key several times.

Looks like browsers treat keyboard events differently from mouse events. – Wladimir Palant Jun 23 at 19:43 On Mac: In Chrome, all keypresses seem to register before the timeout. In Safari 5, the results are inconsistent (I'll press 6 keys and 2-4 will register).

In Firefox 5, results are worse (I'll press 6 keys and 0-2 will register). I'd love to test this on Windows/Linux, because it's entirely possible that the way the OS handles input events makes a big difference here. – Trevor Burnham Jun 23 at 20:18.

You can use dispatchEvent with a custom event name at the end of your function. This won't work on IE, but is still possible; just use fireEvent instead. Take a look at this: jsfiddle.net/minitech/NsY9V/ Click "start the long run", and click on the textbox and type in it.

VoilÃ!

Thanks minitech. – s. Zakharov Jun 21 at 20:51 Not sure how that helps, minitech.

I created a fork here that shows the value of the input element as of afterRun; I get the same value as before the longRun loop. Can you make afterRun execute after the user input has been processed? – Trevor Burnham Jun 21 at 21:17 @Trevor: Hm... in that case, what exactly is the situation?

Is the user going to be typing? Is it acceptable to change the focus? What's the entire situation?

– minitech Jun 21 at 21:20 The problem arose form my work with canvas. Let's say it takes 250ms to render a canvas. I want the user's click and mousemove events during that period to reflect the state that was visible during those 250ms, but I want all click and mousemove events afterward to reflect the newly drawn state.So I want my updateState function to fire after all input that occurred during the canvas rendering period has resolved, but before any further input occurs.

Turns out that's a challenge. – Trevor Burnham Jun 21 at 21:33 @Trevor: Then you can use a state variable. Editing answer.

– minitech Jun 21 at 21:36.

At this point, I'm prepared to say that, regrettably, there is no solution to this problem that will work under all browsers, in every scenario, every time. In a nutshell: If you run a JavaScript function, there's no way to reliably distinguish between input events that the user triggered during that time and those the user triggered afterward. This has interesting implications for JS developers, especially those working with interactive canvases.My mental model of how JS input events work was off the mark.

I'd thought that it went The user clicks a DOM element while code is running If that element has a click event handler, the callback is queued When all blocking code has executed, the callback is run However, my experiments, and those contributed by Wladimir Palant (thanks, Wladimir) show that the correct model is The user clicks a DOM element while code is running The browser captures the coordinates, etc. Of the click Some time after all blocking code has executed, the browser checks which DOM element is at those coordinates, then runs the callback (if any) I say "some time after" because different browsers seem to have very different behaviors for this—in Chrome for Mac, I can set a setTimeout func2, 0 at the end of my blocking code and expect func2 to run after the click callbacks (which run only 1-3ms after the blocking code finished); but in Firefox, the timeout always resolves first, and the click callbacks typically happen ~40ms after the blocking code finished executing. This behavior is apparently beyond the purview of any JS or DOM spec. As John Resig put it in his classic How JavaScript Timers Work: When an asynchronous event occurs (like a mouse click, a timer firing, or an XMLHttpRequest completing) it gets queued up to be executed later (how this queueing actually occurs surely varies from browser-to-browser, so consider this to be a simplification).

(Emphasis mine.) So what does this mean from a practical standpoint? This is a non-issue as the execution time of blocking code approaches 0. Which means that this problem is yet another reason to hew to that old advice: Break up your JS operations into small chunks to avoid blocking the thread.

Web workers, as Useless Code suggested, are even better when you can use them—but be aware that you're foregoing compatibility with Internet Explorer and all major mobile browsers. Finally, I hope browser-makers will move forward on standardizing input events in the future. This is one of many quirks in that area.

I hope Chrome will lead the way to the future: excellent thread isolation, low event latency, and relatively consistent queueing behavior. A web developer can dream, can't he?

Please keep in mind that the blocking JavaScript code could change event handlers or the DOM structure of the document. Also, there can be event listeners on different levels of the document, some of which might or might not be reached depending on whether any listener calls stopPropagation(). So your simple mental model cannot work.

– Wladimir Palant Jun 24 at 2:02 But you can change #2 into "The click event is queued to be processed later" and #3 into "When all blocking code has executed the queued events are processed (hit target determined, appropriate listeners triggered)". With the remark "Timeouts go into the same queue and are guaranteed to run after the events are processed if these events were added to the queue before the timeout". That matches the behavior of Firefox on Windows, too bad that it is apparently different on Mac.

– Wladimir Palant Jun 24 at 2:02 I think we're saying the same thing in different words. When you say "The click event is queued to be processed later," that doesn't sound right to me, because when I hear the words "click event," I think of particular arguments being passed to a particular callback; but as you well know, neither the arguments nor the callback are determined until after #3. (Even e.

TimeStamp, in browsers that support it, matches the time when the first callback was called, not the time that the user clicked something. ) – Trevor Burnham Jun 24 at 16:21 That's because Firefox (and other browsers probably similarly) have a distinction between "native events" and "DOM events". A DOM event is what event listeners see and it isn't created until the event processing starts.

A native event on the other hand isn't much more than what the OS supplies. – Wladimir Palant Jun 24 at 16:29.

You can make the event handlers check to see if a flag is set by func1; if so queue func2 if not already queued. This may either be elegant or ugly depending on the specializedness of func2. (Actually it's probably just ugly.) If you choose this approach, you need some way to hook events, or alternatively your own bindEvent(event,handler,...) function which wraps the handler and binds the wrapped handler.

The correctness of this approach depends on all the events during func1 being queued at the same time. If this is not the case, you can either make func2 idempotent, or (depending on the semantics of func2) put an ugly "cannot be called again for N milliseconds" lock on it.

Suppose the user clicked 10 times during func1. It would be easy to add if (!h) h = setTimeout(func2, 0) to the click callback, but could I be sure that func2 wouldn't fire until after the other 9 clicks (and perhaps other input events) have been resolved? – Trevor Burnham Jun 21 at 21:21 Ah, of course, re-queueing it in every input handler (clearTimeout plus setTimeout) does work: jsfiddle.Net/EJNSu/11 I'll accept this answer if no one has a more elegant solution.

– Trevor Burnham Jun 21 at 21:27 Hmm, I spoke too soon. The example above fails (under Firefox 5 for Mac) if I move the alert code out of the arbitrary 500ms timeout into func2—which means that the clicks are actually firing after func2. See jsfiddle.

Net/TrevorBurnham/j8kVY – Trevor Burnham Jun 23 at 21:11.

How about this: var func1Running = false; var queue = ; function func1() { func1Running = true; // do your stuff func1Running = false; for (var I = 0; I Calee; return; } // do func2Stuff } You can make it a bit easier if you define a func like this: function scheduleFunc(func) { if (func1Running == true) { queuequeue. Length = func; return; } func(); } and pass a non-modified func2 to the scheduleFunc. The only issue here is func2 should have any arguments.

Func1Running will always be false by the time any events occur. Please read John Resig's classic How JavaScript Event Timers Work. – Trevor Burnham Jun 23 at 17:20 @Trevor, so it actually seems that js engines are doing almost the same thing like I am here.

Thanks for the link. – Ivaylo Slavov Jun 24 at 8:03.

Please describe better you scenario. What you need do some time ago I need do something how that was so I build an simple javascript's routine across serialize async call in one sync call. Maybe you could used that added one variant for example that let me show how that work first register all async or sync routines second register end callback third register call's to routines with yours parameters fourth thrown process in your case it neccesary added one call routine and that routine should be UoW of user actions.

Now the main problem is not call to routine in and order of execution if not track changes done by the user first register all async or sync routines second register end callback third register call's to routines with yours paramter --register your first routine --register BlockUi //maybe for not accept more changes in the view --register UiWriter // UoW of change done by user --register you last routine fourth thrown process in real code that is one call dummy's function function Should_Can_Serializer_calls() { RegisterMethods(model); model.Queue. BeginUnitProcess(); //clear stack of execution, y others model.Queue. AddEndMethod(SucessfullEnd); // callback to end routine model.

AbstractCall("func1",1,"edu",15,""); //set routine how first to execute model. AbstractCall("BlockUi"); //track changes and user's actions model. AbstractCall("UiWork"); //track changes and user's actions model.

AbstractCall("func2","VALUE"); //set second routine for execute model.Process(); //throw call } Now the methods should be async for themselves for this you could use that library http://devedge-temp.mozilla.org/toolbox/examples/2003/CCallWrapper/index_en.html so, what do you want do?

I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.

Related Questions