My users are presented a basically a stripped down version of a spreadsheet. There are textboxes in each row in the grid. When they change a value in a textbox, I'm performing validation on their input, updating the collection that's driving the grid, and redrawing the subtotals on the page. This is all handled by the OnChange event of each textbox.
When they click the Save button, I'm using the button's OnClick event to perform some final validation on the amounts, and then send their entire input to a web service, saving it.
At least, that's what happens if they tab through the form to the Submit button.
The problem is, if they enter a value, then immediately click the save button, SaveForm() starts executing before UserInputChanged() completes -- a race condition. My code does not use setTimeout, but I'm using it to simulate the sluggish UserInputChanged validation code:
<script>
var amount = null;
var currentControl = null;
function UserInputChanged(control) {
currentControl = control;
// use setTimeout to simulate slow validation code
setTimeout(ValidateAmount, 100);
}
function SaveForm() {
// call web service to save value
document.getElementById("SavedAmount").innerHTML = amount;
}
function ValidateAmount() {
// various validationey functions here
amount = currentControl.value; // save value to collection
document.getElementById("Subtotal").innerHTML = amount;
}
</script>
Amount: <input type="text" onchange="UserInputChanged(this)">
Subtotal: <span id="Subtotal"></span>
<button onclick="SaveForm()">Save</button>
Saved amount: <span id="SavedAmount"></span>
I don't think I can speed up the validation code -- it's pretty lightweight, but apparently, slow enough that code tries to call the web service before the validation is complete.
On my machine, ~95ms is the magic number between whether the validation code executes before the save code begins. This may be higher or lower depending on the users' computer speed.
Does anyone have any ideas how to handle this condition? A coworker suggested using a semaphore while the validation code is running and a busy loop in the save code to wait until the semaphore unlocks - but I'd like to avoid using any sort of busy loop in my code.
Use the semaphore (let's call it StillNeedsValidating). if the SaveForm function sees the StillNeedsValidating semaphore is up, have it activate a second semaphore of its own (which I'll call FormNeedsSaving here) and return. When the validation function finishes, if the FormNeedsSaving semaphore is up, it calls the SaveForm function on its own.
In jankcode;
function UserInputChanged(control) {
StillNeedsValidating = true;
// do validation
StillNeedsValidating = false;
if (FormNeedsSaving) saveForm();
}
function SaveForm() {
if (StillNeedsValidating) { FormNeedsSaving=true; return; }
// call web service to save value
FormNeedsSaving = false;
}
Disable the save button during validation.
Set it to disabled as the first thing validation does, and re-enable it as it finishes.
e.g.
function UserInputChanged(control) {
// --> disable button here --<
currentControl = control;
// use setTimeout to simulate slow validation code (production code does not use setTimeout)
setTimeout("ValidateAmount()", 100);
}
and
function ValidateAmount() {
// various validationey functions here
amount = currentControl.value; // save value to collection
document.getElementById("Subtotal").innerHTML = amount; // update subtotals
// --> enable button here if validation passes --<
}
You'll have to adjust when you remove the setTimeout and make the validation one function, but unless your users have superhuman reflexes, you should be good to go.
I think the timeout is causing your problem... if that's going to be plain code (no asynchronous AJAX calls, timeouts etc) then I don't think that SaveForm will be executed before UserInputChanged completes.
A semaphore or mutex is probably the best way to go, but instead of a busy loop, just use a setTimeout() to simulate a thread sleep. Like this:
busy = false;
function UserInputChanged(control) {
busy = true;
currentControl = control;
// use setTimeout to simulate slow validation code (production code does not use setTimeout)
setTimeout("ValidateAmount()", 100);
}
function SaveForm() {
if(busy)
{
setTimeout("SaveForm()", 10);
return;
}
// call web service to save value
document.getElementById("SavedAmount").innerHTML = amount;
}
function ValidateAmount() {
// various validationey functions here
amount = currentControl.value; // save value to collection
document.getElementById("Subtotal").innerHTML = amount; // update subtotals
busy = false;
}
You could set up a recurring function that monitors the state of the entire grid and raises an event that indicates whether the entire grid is valid or not.
Your 'submit form' button would then enable or disable itself based on that status.
Oh I see a similar response now - that works too, of course.
When working with async data sources you can certainly have race conditions because the JavaScript process thread continues to execute directives that may depend on the data which has not yet returned from the remote data source. That's why we have callback functions.
In your example, the call to the validation code needs to have a callback function that can do something when validation returns.
However, when making something with complicated logic or trying to troubleshoot or enhance an existing series of callbacks, you can go nuts.
That's the reason I created the proto-q library: http://code.google.com/p/proto-q/
Check it out if you do a lot of this type of work.
You don't have a race condition, race conditions can not happen in javascript since javascript is single threaded, so 2 threads can not be interfering with each other.
The example that you give is not a very good example. The setTimeout call will put the called function in a queue in the javascript engine, and run it later. If at that point you click the save button, the setTimeout function will not be called until AFTER the save is completely finished.
What is probably happening in your javascript is that the onClick event is called by the javascript engine before the onChange event is called.
As a hint, keep in mind that javascript is single threaded, unless you use a javascript debugger (firebug, microsoft screipt debugger). Those programs intercept the thread and pause it. From that point on other threads (either via events, setTimeout calls or XMLHttp handlers) can then run, making it seem that javascript can run multiple threads at the same time.
Related
I've a question/problem with an whileloop
I need to wait until something changes outside the while loop.
Let's say i have this while loop:
window.changeMe = true;
while(window.changeMe){
}
now i have these two options:
Change the changeMe variable via the Console/JavaScript Execution
Change the changeMe variable via an WebSocket Event
but neither is working, if i change the Variable directly, it is not changed.
If i trigger an WebSocket Event its not getting called.
Maybe its BLOCKED.. so is there any other way to change the variable?
I known i can use await and its already working that way, but the problem is that these functions with while are called via an Addon
and using many await's looks kinda ugly for the addon creator :(
an system with setTimeout & Callbacks are also working but also looks kinda ugly..
Yes, you are correct. Having a infinite while loop will prevent executing any other code from javascript event loop which occupies the main thread.
In order to imitate the same behavior you can implement your own while loop that is friendly to asynchronous events and external code execution. You have to use:
tail recursion in order to minimize the memory footprint,
setTimeout as a mechanism to allow other parts of your code to run asynchronously.
EXAMPLE:
window.changeMe = true;
let stop = setTimeout(() => { console.log("External change stop"); window.changeMe = false; }, 4000)
var whileLoop = () => {
console.log("Inside: ", window.changeMe)
return window.changeMe
? setTimeout(() => { whileLoop(); }, 0)
: false
}
whileLoop()
console.log("Outside: ", window.changeMe)
Here is a fiddle:
https://jsfiddle.net/qwmosfrd/
Here is a setInterval fiddle:
https://jsfiddle.net/2s6pa1jo/
Promise return value example fiddle:
https://jsfiddle.net/0qum6gnf/
JavaScript is single-threaded. If you have while (true) {}, then nothing else outside the while loop can change the state of your program. You need to change your approach. You probably want to set up event listeners instead or put this inside an async function so you can use await to release execution, or some other asynchronous API. But plain vanilla while () {} is synchronous and cannot be affected by other things while it is running.
You can't use a while loop in that way in nodejs.
Nodejs runs your Javascript in a single thread and the overall architecture of the environment is event driven. What your while loop is doing is a spin loop so while that loop is running, no other events can ever run. You have to return control back to the event loop before any other events can run. That means that timers, network events, etc... cannot run while your spin loop is running. So, in nodejs, this is never the right way to write code. It will not work.
The one exception could be if there was an await inside the loop which would pause the loop and allow other events to run.
So, while this is running:
while(window.changeMe){
}
No other events can run and thus nothing else gets a chance to change the changeMe property. Thus, this is just an infinite loop that can never complete and nothing else gets a chance to run.
Instead, you want to change your architecture to be event driven so that whatever changes the changeMe property emits some sort of event that other code can listen to so it will get notified when a change has occurred. This can be done by having the specific code that changes the property also notify listeners or it can be done by making the property be a setter method so that method can see that the property is being changed and can fire an event to notify any interested listeners that the value has changed.
I have a sort of a strange problem with the structure of my JS code. It is a convertor from Brainfuck to JavaScript. Then it runs the code it has generated.
With this in mind, I have noticed a problem with the prompt(); command. It seems to, in some situations, show the prompt box before the previous command (changing the DOM) finishes.
My solution is to show an <input type="text"> box, initially set to display:none in CSS. However, I need a way to wait for the input to change. I tried this code:
while (document.getElementById("input") == ""){}
But this freezes the DOM. I need a way to passively wait for any user input, then continue with the script.
Keep in mind, I can't use onkeydown, external functions, etc.
Thanks!
There's no way to stop script execution besides using native functions like alert, confirm, prompt.
Try wrapping your prompt inside a setTimeout. Start with a value of 0 to see if it works.
setTimeout(function () {
prompt('your prompt');
}, 0);
Using setTimeout with a value of 0 pushes the execution of the prompt to the back of the event loop, and may possibly run after your DOM updates.
Read more about this technique here: https://www.quora.com/What-does-setTimeout-with-a-0ms-delay-do
To wait for user input in a non-blocking way, you need to run asynchronous code. This you do by providing a callback function which should be called when a certain event takes place. In your case, this could be when the input value is submitted:
var inp = document.getElementById("inp");
inp.addEventListener('change', function(e) {
var value = this.value;
alert('you entered: ' + value);
// all code that needs the value, should come here,
// or should be called from here.
});
Exit field to submit value:
<input id="inp">
My program has two buttons. One is for execute other program by using jquery load function. Whenever I click execute button, it runs some other program abc.php using load function for n times, with some time gap like k mins. These n and k will be filled with html inputs. Using jquery, will retrieve these and passing to that program file in url.
To call this function setTimeout was used.
Second one is for cancel execution.
Now my doubt is, suppose I want to stop that execution with cancel button. Is there any way to stop it ?
I would do this using boolean variable.
For example: Consider a method, perform logging.
fun () {
console.log("prints");
}
I would change it has
fun (isExecute) {
if (isExecute) {
console.log("prints");
}
}
Run fun (true); cancel fun (false);
Updates:
It seems you use setTimeout(), then it is too easy without above approach.
Run var inter = setTimeout(fun); cancel clearTimeout(inter);
FYI: The reason for assigning to a variable inter is then only you can clear this time interval.
set time for function:
timer = setTimeout(function(){$('#submenu').hide();},5000);
stop a function
clearTimeout(timer);
This is a really basic JavaScript question and probably duplicate, but I don't know the answer!
I have code as follows:
function userlist_change(myval, function_type) {
// relatively slow code involving Ajax call
// based on Ajax results, change some client-side stuff
}
$("#subjectlist").change(function() {
userlist_change($("#subjectlist").val(), 'change');
}).change();
$("#subjectlist").keypress(function() {
userlist_change($("#subjectlist").val(), 'keypress');
});
I have the problem that if the .change() event is called, the userlist_change function kicks off, and it's relatively slow. If the user changes the list again (e.g. by typing), my code waits for userlist_change to complete before restarting it with the new value.
This looks quite odd in the UI, as it can take a few seconds for anything to change client-side - and sometimes the results of the first call only appear after the user has already made a second call.
Is there any way I can interrupt any existing userlist_change process when the .change() or `keypress() event is fired?
[EDIT] What would be ideal is a simple 'kill any running functions with this name' command - is this possible? Or do I really have to fiddle around with timers?!
you can store last request time in a global variable, and store a request time in each ajax request, so that when you are just showing the result of first request, if the global last request time is greater than request, request time, you should show, other wise not. For example:
var lastRequestTime;
function userlist_change(myval, function_type,requestTime) {
// relatively slow code involving Ajax call
// based on Ajax results, change some client-side stuff
if(lastRequestTime <= requestTime){
//show
}
}
$("#subjectlist").change(function() {
lastRequestTime = new Date();
userlist_change($("#subjectlist").val(), 'change',lastRequestTime );
}).change();
$("#subjectlist").keypress(function() {
lastRequestTime = new Date();
userlist_change($("#subjectlist").val(), 'keypress',lastRequestTime );
});
You should use throttling of event. It is quite easily done with RX for JavaScript, but library is quite complicated. You can try filter value with timer.
Here is useful plugin for throttling: http://benalman.com/projects/jquery-throttle-debounce-plugin/
I am porting an old game from C to Javascript. I have run into an issue with display code where I would like to have the main game code call display methods without having to worry about how those status messages are displayed.
In the original code, if the message is too long, the program just waits for the player to toggle through the messages with the spacebar and then continues. This doesn't work in javascript, because while I wait for an event, all of the other program code continues. I had thought to use a callback so that further code can execute when the player hits the designated key, but I can't see how that will be viable with a lot of calls to display.update(msg) scattered throughout the code.
Can I architect things differently so the event-based, asynchronous model works, or is there some other solution that would allow me to implement a more traditional event loop?
Am I making sense?
Example:
// this is what the original code does, but obviously doesn't work in Javascript
display = {
update : function(msg) {
// if msg is too long
// wait for user input
// ok, we've got input, continue
}
};
// this is more javascript-y...
display = {
update : function(msg, when_finished) {
// show part of the message
$(document).addEvent('keydown', function(e) {
// display the rest of the message
when_finished();
});
}
};
// but makes for amazingly nasty game code
do_something(param, function() {
// in case do_something calls display I have to
// provide a callback for everything afterwards
// this happens next, but what if do_the_next_thing needs to call display?
// I have to wait again
do_the_next_thing(param, function() {
// now I have to do this again, ad infinitum
}
}
The short answer is "no."
The longer answer is that, with "web workers" (part of HTML5), you may be able to do it, because it allows you to put the game logic on a separate thread, and use messaging to push keys from the user input into the game thread. However, you'd then need to use messaging the other way, too, to be able to actually display the output, which probably won't perform all that well.
Have a flag that you are waiting for user input.
var isWaiting = false;
and then check the value of that flag in do_something (obviously set it where necessary as well :) ).
if (isWaiting) return;
You might want to implement this higher up the call stack (what calls do_something()?), but this is the approach you need.