Just last week, I was teaching a group of back-end developers how to use Angular to develop front ends. One question that came up, which did suprise me a bit, was how to deal with race conditions and concurrency in JavaScript.
I’m glad they asked, because it’s a good question that never occurred to me. The JavaScript runtime, of course, is single-threaded. You might use Web Workers to get multiple threads, but they use an Actor model, so there’s no shared state, and thus no need for any sort of locking.
Chris R’s team did have a need for locking. Specifically, their .NET backend needed to run a long-ish bulk operation against their SqlServer. It would be triggered by an HTTP request from the client-side, AJAX-style, but only one user should be able to run it at a time.
Someone, for some reason, decided that they would implement this lock in front-end JavaScript, since that’s where the AJAX calls were coming from..
var myMutex = true; //global (as in page wide, global) variable
function onClickHandler(element) {
if (myMutex == true) {
myMutex = false;
// snip...
if ($(element).hasClass("addButton") == true) {
$(element).removeClass("addButton").addClass("removeButton");
// snip...
$.get(url).done(function (r) {
// snip... this code is almost identical to the branch below
setTimeout("myMutex = true;", 100);
});
} else {
if ($(element).hasClass("removeButton") == true) {
$(element).removeClass("removeButton").addClass("addButton");
// snip...
$.get(url).done(function (r) {
// snip... this code is almost identical to the branch above
setTimeout("myMutex = true;", 100);
});
}
}
}
}
You may be shocked to learn that this solution didn’t work, and the developer responsible never actually tested it with multiple users. Obviously, a client side variable isn’t going to work as a back-end lock. Honestly, I’m not certain that’s the worst thing about this code.
First, they reinvented the mutex badly. They seem to be using CSS classes to hold application state. They have (in the snipped code) duplicate branches of code that vary only by a handful of flags. They aren’t handling errors on the request- which, when this code started failing, made it that much harder to figure out why.
But it’s the setTimeout("myMutex = true;", 100);
that really gets me. Why? Why the 100ms lag? What purpose does that serve?
Chris threw this code away and put a mutex in the backend service.