I'm creating a website that will feature news articles. These articles will appear in two columns at the bottom of the page. There will be a button at the bottom to load additional news stories. That means that I need to be able to specify what news story to load. Server-side, I'm simply implementing this with a LIMIT clause in my SQL statement, supplying the :first parameter like so:
SELECT *
FROM news
ORDER BY `date` DESC
LIMIT :first, 1
This means that, client-side, I need to keep track of how many news items I've loaded. I've implemented this by having the function to load new information be kept in an object with a property holding the number of items loaded. I'm worried that this is somehow a race condition that I am not seeing, though, where my loadNewInformation() will be called twice before the number is incremented. My code is as follows:
var News = {
newInfoItems: 0,
loadNewInformation: function(side) {
this.newInfoItems += 1;
jQuery.get(
'/api/news/'+ (this.newInfoItems - 1),
function(html) {
jQuery('div.col'+side).append(html);
}
);
}
}
On page load, this is being called in the following fashion:
News.loadNewInformation('left');
News.loadNewInformation('right');
I could have implemented this in such a way that the success handler of a first call made another AJAX request for the second, which clearly would not be a race condition...but this seems like sloppy code. Thoughts?
(Yes, there is a race condition.)
Addressing Just the JavaScript
All JavaScript code on a page (excluding Web-Workers), which includes callbacks, is run "mutually exclusive".
In this case, because newInfoItems is eagerly evaluated, it is not even that complex: both "/api/news/0" and "/api/news/1" are guaranteed to be fetched (or fail in an attempt). Compare it to this:
// eager evaluation: value of url computed BEFORE request
// this is same as in example (with "fixed" increment order ;-)
// and is just to show point
var url = "/api/news/" + this.newInfoItems
this.newInfoItems += 1;
jQuery.get(url,
function(html) {
// only evaluated on AJAX callback - order of callbacks
// not defined, but will still be mutually exclusive.
jQuery('div.col'+side).append(html);
}
);
However, the order in which the AJAX requests complete is not defined and is influenced by both the server and browser. Furthermore, as discussed below, there is no atomic context established between the server and individual AJAX requests.
Addressing the JavaScript in Context
Now, even though it's established that "/api/news/0" and "/api/news/1" will be invoked, imagine this unlikely, but theoretically possible situation:
articles B,A exist in database
browser sends both AJAX requests -- asynchronously or synchronously, it doesn't matter!
an article is added to the database sometime between when
the server processes the news/0 request, and
the server processes the news/1 request
Then, this happens:
news/0 returns article B (articles B,A in database)
article C added
news/1 returns article B (articles C,B,A in database)
Note that article B was returned twice! Oops :)
So, while the race-condition "seems fairly unlikely", it does exist. A similar race condition (with different results) can occur if news/1 is processed before news/0 and (once again) an article is added between the requests: there no atomic guarantee in either case!
(The above race condition would be more likely if executing the AJAX requests in-series as the time for a new article being added is increased.)
Possible Solution
Consider fetching say, n (2 is okay!) articles in a single request (e.g. "/api/latest/n"), and then laying out the articles as appropriate in the callback. For instance, the first half of the articles on the left and the second half on right, or whatever is appropriate.
As well as eliminating the particular race-condition above by making the single request an atomic action -- with respect to article additions -- it will also result in less network traffic and less work for the server.
The fetch for the API might then look like:
SELECT *
FROM news
ORDER BY `date` DESC
LIMIT :n
Happy coding.
Yes, technically, this could create a race condition of sorts. The calls are asynchronous, and if the first got held up for some reason, the second could return first.
However, as you don't have a great deal that goes on in your callback functions that depend on the presence of the other 'side' being populated I don't see where it should cause you too much grief.
Shouldn't be any race conditions, must be something else wrong in your code. The counter is incremented before your .get() call, so prior to each get the counter should be incremented correctly. Short example to demostrate that it works when called sequentially: http://jsfiddle.net/KkpwF/
I reckon you're hinting at the newItemInfo counter?
Observations:
You're calling News.loadNewInformation twice, with a callback function which will be executed on AJAX completion.
The callback method you provide does not alter the News object.
In this case, you don't have to worry. The second call to News.loadNewInformation will only be executed once the first call completes - that is, excluding executing the callback method. Hence your newItemInfo counter will contain the correct value.
Try this:
loadNewInformation: function(side) {
jQuery.get(
'/api/news/'+ (this.newInfoItems++),
function(html) {
jQuery('div.col'+side).append(html);
}
);
}
Related
I process thousands of points asynchronously in ArcGIS JS API. In the main function, I call functions processing individual features, but I need to finalize the processing when all the features are processed. There should be an event for this, though I didn't find any and I'm afraid it even doesn't exist - it would be hard to state that the last item processed was the last of all. .ajaxStop() should do this, but I don't use jQuery, just Dojo. Closest what I found in Dojo was Fetch and its OnComplete, but as far as I know it's about fetching data from AJAX, not from other JS function.
The only workaround idea I have now is to measure how many features are to be processed and then fire when the output points array reaches desired length, but I need to count the desired number at first. But how to do it at loading? Tracking the data to the point where they are read from server would mean modifying functions I'm not supposed to even know, which is not possible.
EDIT - some of my code:
addData: function (data) {
dojo.addOnLoad(
this.allData = data,
this._myFunction()
);
},
Some comments:
data is an array of graphics
when I view data in debugger, its count is 2000, then 3000, then 4000...
without dojo.addOnLoad, the count started near zero, now it's around 2000, but still a fraction of the real number
_myFunction() processes all the 2000...3000...4000... graphics in this._allData, and returns wrong results because it needs them all to work correctly
I need to delay execution of _myFunction() until all data load, perhaps by some other event instead of dojo.addOnLoad.
Workarounds I already though of:
a) setTimeout()
This is clearly a wrong option - any magic number of miliseconds to wait for would fail to save me if the data contains too much items, and it would delay even cases of a single point in the array.
b) length-based delay
I could replace the event with something like this:
if(data.length == allDataCount) {
this._myFunction();
}
setTimeout(this._thisFunction, someDelay);
or some other implementation of the same, through a loop or a counter incremented in asynchronously called functions. Problem is how to make sure the allDataCount variable is definitive and not just the number of features leaded until now.
EDIT2: pointing to deferreds and promises by #tik27 definitely helped me, but the best I found on converting synchronous code to a deferred was this simple example. I probably misunderstood something, because it doesn't work any better than the original, synchronous code, the this.allData still can't be guaranteed to hold all the data. The loading function now looks like this:
addData: function (data) {
var deferred = new Deferred();
this._addDataSync(data, function (error, result) {
if (error) {
deferred.reject(error);
}
else {
deferred.resolve(result);
}
});
deferred.promise.then(this._myFunction());
},
_addDataSync: function (data, callback) {
callback(this.allData = data);
},
I know most use cases of deferred suppose requesting data from some server. But this is the first time where I can work with data without breaking functions I shouldn't change, so tracking the data back to the request is not an option.
addonload is to wait for the dom.
If you are waiting for a function to complete to run another function deferred/promises are what is used.
Would need more info on your program to give you more specific answers..
I sort of solved my problem, delaying the call of my layer's constructor until the map loads completely and the "onUpdateEnd" event triggers. This is probably the way how it should be properly done, so I post this as an answer and not as an edit of my question. On the other hand, I have no control over other calls of my class and I would prefer to have another line of defense against incomplete inputs, or at least a way to tell whether I should complain about incomplete data or not, so I keep the answer unaccepted and the question open for more answers.
This didn't work when I reloaded the page, but then I figured out how to properly chain event listeners together, so I now can combine "onUpdateEnd" with extent change or any other event. That's perfectly enough for my needs.
I am having some trouble with a bit of code. I have a function that does some stuff to some data, calls a remote system (activating a script on that system and passing in the data), and then makes another call to the same system to activate a different script (which acts on the data saved above). The problem is that the 1st call to the remote system appears to get lost in the execution.
This is being run in Safari, uses jquery; the function is tied to a button click, which is defined in the javascript code with an onclick function (i.e. it is not defined in the html button definition).
Here's a rough breakdown of the function (cleaned out for viewing purposes - I hope I left enough to make it clear):
function compareJSON() {
// loop through the objects, testing and changing data
// ...
dataSession=({ //build object for output });
$.each( dataSession.chapters , function( indexC, value ) {
//compare objects to some others, testing and changing data
});
// ...
//Call remote script on other system
urlString="url://blah.dee.com/Blar?script=SaveJSON&$JSONobject=";
window.location= urlString + JSON.stringify(dataSession);
//Call remote script on other system
window.location="url://blah.dee.com/Blar?script=EditJSON";
}
The last three lines of code are the two calls. It uses the window.location to actually trigger the remote system, passing the data through the URL. But I need BOTH scripts to get called and run. It appears that only the LAST script in the sequence ever gets run. If I switch them around it remains whatever is in last place.
Is there something about the window.location that doesn't actually process until the end of the function?
This script actually used to be a series of separate function calls, but I figured I was running into asynchronous execution that was causing the various script calls to not register. But once I put the code into this single function, it was still happening.
Any clues would be helpful.
Thanks,
J
Modifing the value of window.location is reserved exclusively for instances in which you'd like to cause a browser redirect.
It looks like you want to trigger a page request instead. You say you already have jQuery loaded, if so, you can trigger such a request using jQuery.get or a similar function.
For example:
// Loads the myscript.php page in the background
$.get('myscript.php');
// You can also pass data (in the form of an object as the second argument)
$.get('myscript.php', { name: "John", time: "2pm" });
When I do some asynchronous call in javascript, I put callback function into some queue, right?
May I control the queue position where function is put somehow? Explicitly or by some priority?
UPDATE 1
My question is not only about AJAX. There are several places where asynchronous calls used in JS (I may be wrong):
event handlers
XMLHttpRequest calls
setTimeout()/setInterval()
According to replies, I consider the general answer is NO, i.e. message queue is not accessible from javascript.
No. The browser doesn't maintain an actual queue of asynchronous requests. Not all browsers necessarily handle it the same way, but conceptually the browser simply suspends the thread that is handling the request until the response to that request returns or the timer expires. At that point, it resumes executing the thread in the callback with the data supplied by the response. I'm sure that I've oversimplified it, but with any more detail the answer would probably differ depending on which browser we're talking about.
If you want an ordering for your AJAX requests, you'll need to build the mechanism or find an existing JavaScript plugin/library to do that. The simplest way is probably just to make subsequent requests in the callbacks of requests that should take precedence. That way you know that they will occur later than the ones that should go before them. Timers, of course, invoke their callbacks when they expire. You could order them, if desired, by being careful with the expiration times.
One thing you can do is include a sequence number in every request (and response) and use the same callback handler for every AJAX call. This way your callback handler can check the sequence number returned and manage the order of execution of callbacks.
i.e. something like:
every AJAX request will be like /getUsers?seq=1&..., /getProps?seq=2&... etc..
every AJAX response (assuming JSON here) will return the seq num is received with the request, i.e.
result {
seq: 2,
data: ...
}
then your callback handler will look something like:
var responseQ = [];
function handler(response) {
// .. code to push the response into the responseQ in increasing order of seq num ..
}
and you can have a queue processor execute every N seconds, which checks for outstanding responses in the Q and processes them accordingly..
i.e. something like:
setInterval(function() {
// get last item from responseQ
// check its seq num
// execute if appropriate
}, 10000);
I thought I had this mess sorted out in my head but for some odd reason its not working.
If you declare a variable outside of a function / scope and refer to it without the var inside a function then it changes the variable declared previously... right?
however, the first alert returns the correct price, but the second (last) alert returns 0. What am I doing wrong?
//get pricing
var price=0;
var modelid = $("#model_input").val();
var inCode = $("#code_input").val();
$.get("getpricing.php", { 'modelid': modelid ,'code' : inCode }, function(data){
price = data;
alert(price);
});
alert(price);
You are using an Ajax request.
Those, unkess specified otherwise, are Asynchrounous : they are executed in the background, without stopping the execution of the rest of the script.
So, the alert on the last line of code is executed before the Ajax request is finished ; which means price is still 0 at that time.
One way to change that would be to use a synchronous request (see async option) ; but I strongly advise against it ; quoting the doc :
By default, all requests are sent
asynchronous (i.e. this is set to true
by default). If you need synchronous
requests, set this option to false.
Note that synchronous requests may
temporarily lock the browser,
disabling any actions while the
request is active.
And you definitly don't want your application to freeze the whole browser!
You should re-think the way your application is designed, in this case : you can only use the "price" information after the Ajax request is finished -- which probably means you should place more code inside the function called on its success : just placing code after the $.get is not enough.
httprequests using .get are asynchronous, so the first alert (the second declared) will alert the original value as the callback for the request has not triggered yet.
A little (!) bit of background before I can get to the question :
I am implementing a web based search solution. Technology used: javascript (jquery), .net, html etc. etc.
All my web service calls are done through javascript (cross domain ws call). I have few sequential web service calls which all have different success callback function.
I am not able to digest - when i call those ws individually in seperate places they are returning me proper results but sequentially sometime they are giving and sometime not.
sample code: this is not giving expected results all the time.
function submitSearchRequest(_queryString, Stores) {
if (Stores[1].length>0) {
//generate 'searchRequestForArtifact' request object
getSearchResponse("successcallForArtifact", _searchRequestForArtifact);
}
if (Stores[2].length > 0) {
//generate 'searchRequestForPerson' request object
getSearchResponse("successcallForPerson", _searchRequestForPerson);
}
}
function successcallForArtifact(response)
{
//show the results
}
function successcallForPerson(response)
{
//show the results
}
}
If you need sequentially you will need to kick off each search only after one has returned. Currently you are making async calls, meaning it gets kicked off then continues with the code. Currently if the second call is simply faster the order will be off. You will either need to make a sync call or simply have the order enforced by calling the second search from the success function for the artifact.
If you are using JQuery which it seems you are you can set the async parameter to false which will force the order you want but it will slow the overall performance of your page. See this question.