How to store ajax values in a queue - javascript

I want to display some data which comes from my db using jQuery.ajax() (i.e. each one contains a title and a description) and I want this process to be done on a regular basis, say setInterval(func, 5000).
Now what I actually need is a js container (an array, a variable, whatever) in which I can store these items to and using another function, I want them to be displayed every 2 seconds.
So, in other words:
We have to have two functions and a container, if you will. The ajax function is fired every 5 seconds and appends its data (if any) into the container (a queue).
On the other hand, the second function displays the content of our container every 2 seconds and removes the displayed items from the container of course.
How can I implement this?

var queue = [];
function ajaxCall() {
$.when($.ajax(...)).done(function(data) {
///assuming your data json's outermost structure is an array
while(data[0]) {
queue.push(data.shift());
}
})
}
function publisher() {
var item = queue.shift();
if(item) {
//do your gubbins here
}
}
setInterval(ajaxCall,5000);
setInterval(publisher, 2000);

Why not using an Array. It can store a string, an object...
Take a look at this great blog post.
If you need queue, this post may help you....

first, set up a container like this one :
<div id="container">
<ul>
</ul>
</div>
then, in your .js :
//step 1. define global Array to hold the values
$(document).ready(function(){
var items = new Array();
//step 3. call the ajax every 5 second
var updateInterval = setInterval(function() {
get_the_data();
//don't forget to empty the array
items = [];
}, 5000);
//step 4. so now, with the data got refresh every 5 second, you just have to append this data into container every 2 second. use the .html method
//logically, in first two times invoke to this method (4 seconds) the container would get same data from array, but in third invocation (at 6 seconds) the container get the fresh one, and so on...
var insertToContainer = setInterval(function() {
for(i=0;i<items.length;i++){
$('#container ul').html('<li>' + items[i] + '</li>')
}
}, 2000);
});
//step 2. set up your ajax call
function get_the_data()
{
$.ajax({
//set your ajax code here...
success:function(data){
//on success put the data into the global array
for(i=0;i<data.length;i++){
items.push(data[i]);
}
}
});
}
I hope this would work for your project. Cheers :-)
PS: frankly speaking, I don't know why you wanted such implementation (i mean, 2 different update functions with different interval value), the more simple way is using just one invocation to the ajax call every 2 seconds as long as display it in container using .html , but it's just my opinion, It should be more complex logic behind your app. :-)

Related

Auto refresh sapui5 table after certain user specified interval

I have a SAPUI5 table. What I want is to provide a text field, where user can enter time interval (like 3 mins), and the table will get refreshed automatically by itself after 3 mins. Moreover, if he provides new value (e.g. 8 mins), table will now get refreshed after 8 mins.
Can anyone give some ideas how can I achieve this? Thanks in advance.
As #sirion said, it depends a little on what table you have, how you want your scroll to behave and so on. I think it is inevitable that your table will "nudge" in some way or another if e.g. new lines get inserted in the table between refreshes or if some lies are deleted.
Nevertheless, I would say that the best option is to get the ListBinding from the table (as sirion said) and do a refresh on this binding:
onRefreshTriggered: function () {
this.byId("myTable").getBinding("items" /* or "rows" */).refresh();
}
It might also be an idea to refresh the element binding for each row, then you would surely not have problems with the scrolling (but you will have problems in case a row is deleted or added).
onRefreshTriggered: function () {
(this.byId("myTable").getItems() || []).forEach(function (oItem){
oItem.getElementBinding(/* model name */).refresh();
});
}
For doing the periodical triggering part, I would use the sap.ui.core.IntervalTrigger class. It is fairly easy to use:
// e.g. in onInit:
this._trigger = new IntervalTrigger(3 * 60 * 1000 /* initial interval */)
this._trigger.addListener(this.onRefreshTriggered, this);
// in a separate method, e.g. as a input field change event
onIntervalChange: function(oEvent) {
var iInterval = parseInt(oEvent.getSource().getValue(), 10);
this._trigger.setInterval(iInterval * 60 * 1000);
}
It depends on what kind of table you are using.
You can always just call oTable.getBinding("x").refresh(), with "x" being "items" or "rows". But that might lead to the table forgetting the scroll position if not everything is shown.
So the "real" solution would be to find out what slice of data is currently shown and then read exactly the same data from the back-end again. Using the read-method on the v2.ODataModel, the model stores the fresh data and updates the property bindings in the table as well.
window.setInterval can be used to call functions at a specific time interval.
window.clearInterval can be used to terminate it.
Attach a change handler to your text, clear any existing intervals and start a new interval with the new time.
<Input change="onInputIntervalChange" />
onInputIntervalChange: function(oEvent) {
var sIntervalInMinutes = oEvent.getParameter("newValue");
var iIntervalInMinutes = parseInt(sIntervalInMinutes);
var iIntervalInMillisec = iIntervalInMinutes * 60 * 1000;
window.clearInterval(this._intervalId);
this._intervalId = window.setInterval(function(){
// refresh your table
}.bind(this), iIntervalInMillisec);
}
Keep in mind that the first refresh will happen after the time interval has passed. If you want to refresh the table immediately when the user changes the value, call your refresh method directly before setInterval.

Javascript callbacks gets processed faster than others

I'm using the javascript sdk plugin for facebook to create a feed on my webpage.
The problem is that sometimes during load the feed gets unordered, even if i have setup a callback chain.
I think it gets unordered because sometimes the "second" async call gets processed faster than the "first" async call.
This is the first time i've been using callbacks, am i doing it right?
How can i solve the feed gets unordered if some calls finish faster than others?
The code below is only the relevant code and is under working status.
function initFeed(){
FB.api('/{id}/feed', function(response){
var feedArray = response.data;
$.each(feedArray, function(){
var $this = $(this)[0]; //Status Object for single Status in Feed
setStatus($this, processStatus); //processStatus is function defined below
});
});
}
function setStatus(statusObject, callbackProcessStatus){
FB.api("/{personId}?fields=id,link,name,picture",
function (response) {
var html = /* Generates html based from statusObject and response */
callbackProcessStatus(html);
});
}
function processStatus(html){
$('#fb-status-wrapper').append(html);
}
(was uncertain on the title of this post, please edit if you think it is not descriptive enough)
Best regards
This is a somewhat common problem with parallel async calls. The simplest solution requires promises. I recommend the Bluebird promise library, but most will do fine.
var fbApi = function(url){
return new Promise(function(resolve, reject){
FB.api(url, function(resp){ resolve(resp); });
});
}
function setStatus(statusObject){
return fbApi("/{personId}?fields=id,link,name,picture")
.then(function(response){
var html = ...;
return html;
});
}
function getFeedItemPromises(){
return fbApi("/{id}/feed").then(function(response){
return response.data.map(function(item){
});
});
}
Depending on your needs, initFeed could be one of these. The first renders the feed when all items are available, and the second renders it when each item is available, but enforces the order.
function initFeed(){
return Promise.all(getFeedItemPromises())
.then(function(itemsHtml){
// append all of the items at once
$('#fb-status-wrapper').append(itemsHtml.join("\n"));
});
}
Or this which ensures the order, but eagerly appends items to the feed, after all previous items have been added.
function initFeed(){
function renderItem(html){
$('#fb-status-wrapper').append(html);
}
// reduce can be used to chain promises in sequence
return getFeedItemPromises().reduce(function(p, nextPromise){
return p.then(function(){ return nextPromise })
.then(renderItem);
}, Promise.resolve())
}
An alternative would be to create a div for each item which acts as a placeholder, keep those in an array, and fill them in when each resolves. This works especially well if you know the height of the items beforehand, and fade them in when they load. From a UX perspective, this is the best in my opinion.
I would not recommend the above if you don't know the heights of items, as it'll cause headache inducing shifting of items as new ones are inserted.
Indeed you cannot rely on the order in which the requests will finish. The only way to be sure, is to only call the second one if the first one is done. But that will slow down the loading quite a lot.
Another possibility is to remember for each request which one it is, and insert the items in the right order (insert before a 'later' one, even if that one was received earlier).
I think the easiest way to do that, is to make placeholders for the items inside the each loop, so the placeholders are inserted in the right order. When the requests return, you just place the responses in the right placeholder.
It could look somewhat like this. 2 extra lines and a couple of tiny changes. I couldn't test this without the API, but I hope you get the idea.
function initFeed(){
FB.api('/{id}/feed', function(response){
var feedArray = response.data;
$.each(feedArray, function(index){
var $this = $(this)[0]; //Status Object for single Status in Feed
// Make a container per item inside the wrapper.
var $itemContainer = $('<div></div>');
$('#fb-status-wrapper').append($itemContainer);
// Pass the container to the api function.
setStatus($this, processStatus, $itemContainer); //processStatus is function defined below
});
});
}
function setStatus(statusObject, callbackProcessStatus, $container){
FB.api("/{personId}?fields=id,link,name,picture",
function (response) {
var html = /* Generates html based from statusObject and response */
// Pass the item place holder/container to the processing procedure.
callbackProcessStatus(html, $container);
});
}
function processStatus(html, $container){
$container.append(html);
}

Two methods of looping through Ajax requests for jQuery Then When - which to use?

I've got a deceptively simple blog project in the works, where I'm trying to bring together Isotope Jquery (for layout/filtering/sorting), Infinite Scroll, and dynamic loading of all blog excerpts via Ajax (so filtering and sorting is applied to all excerpts before the user scrolls down the page (after which time they're loaded into the dom and then accessible)).
This question primarily deals with getting the blog post excerpt data via Ajax, to then be passed into Isotope filtering code. I'm not sure of the best way to do this, but am currently trying to loop through each page (of blog posts excerpts) with an ajax request and then access the data as one whole.
I've come across two different methods to loop through the ajax requests, each using then when jquery statements. The first is using the method give in this SO answer, the other is simply putting the entire then when statement inside of an $.each statement.
Method 1:
var pageCount = 15;
var pageCountArray = [];
for (var i = 1; i != pageCount; ++i) pageCountArray.push(i);
var deferreds = [];
$(pageCountArray).each(function() {
var pageNumber = this;
deferreds.push(
$.get('/page/' + pageNumber)
)
$.when.apply($, deferreds)
.then(function(data){
console.log(data);
// this outputs data as a string from the first page, then a list of objects
console.log(typeof(data));
// string
// 13 - object
});
});
Slight aside: Any ideas as to why this is outputting one string and then objects?
Method 2:
var pageCount = 15;
var pageCountArray = [];
for (var i = 1; i != pageCount; ++i) pageCountArray.push(i);
$(pageCountArray).each(function(data) {
var pageNumber = this;
$.when(
$.get('/page/' + pageNumber)
).then(function() {
console.log(data);
// this outputs 14 strings of data
console.log(typeof(data));
// 14 - string
})
});
I haven't yet figured out how to incorporate the Ajaxed data into my Isotope filter function, but I think I'll need to parse this into HTML first. Still getting my footing with javascript... in this case is one of these data types (objects vs strings) easier to parse into HTML? I suppose that's the key to my answer?
Much obliged for insights.
PS: Bonus points for anyone who might know of a better way to achieve this in a different way that somehow dovetails into Isotope/Infinite Scroll nicely (perhaps in a way that's more intended to play nice with these plugins... I've been unsuccessful in my searching).
PPS: The second method feels much cleaner... anyone know of a reason that it's not a good approach (using when then inside of an .each loop)?
Wow, this is a largely scoped question no wonder there aren't any responses. This is a massive question so I will do my very best to help. I have created many sites that include the sort/filtering of Isotope while using AJAX preload's with infinite scrolling so here is one of the simplest examples I have already written out...
First I must mention that this whole thing works much better with David DeSandro's ImagesLoaded plugin. This is mostly because it allows you to place a callback function (function to be executed once an event occurs) attached to the loading event of the final image in a given container. Wow that was wordy. How to put that better... It basically asks the container, are you done loading yet? No? How about now? You're loaded? Ok please do this function now then...
With that being implemented I would start with this code in my onLoad event like so...
$(function() {
extendJQ_PreLoad(); //I Will Get To This Function In A Min
//Use ImagesLoaded Plugin To Control Load Time Sync
$(container).imagesLoaded(function() {
cont.isotope({
itemSelector: ".box", //This is the class I use on all my images to sort
layoutMode: "masonry",
isOriginLeft: true,
isFitWidth: true,
filter: "*",
masonry: {
columnWidth: ".box"
}
});
preLoadNextImgSet(); //I Will Get To This Function In A Min
});
});
Ok so let's break this down. The ImagesLoaded plugin stops the Isotope plugin instantiation from happening before there are images present to sort/filter/load and/or handle. This is step 1. Step 2 would be to then start looking at the actual isotope plugin instantiation. I am telling it to use Masonry plugin as its layout style and then I am passing in an object literal with options under the array key 'masonry'. The array key here that is named masonry is the same as any instantiation you would have normally done in the past with the stand alone Masonry plugin (non-isotope or isotope-2).
Step 3 to look at here would be my beginning call to extendJQ_PreLoad();. This function is the function I wrote to let JQuery know that I need to extend it's core functionality in order to capacitate preloading any images I give it, as an array. Like so...
function extendJQ_PreLoad() {
$.preloadImages = function(args) {
for (var i = 0; i < args.length; i++) {
$("<img />").attr("src", args[i]);
}
}
} //end function
This is just a simple iterator and nothing fancy, it allows the images to be preloaded by using a neat trick associated with the DOM. If you load images in this way it loads then into memory but not into the DOM meaning it is loaded and hidden. Once you then insert this image anywhere it will insert very quickly as it is now loaded in cache and awaiting placement. You can view more about this here.
Finally the last to look at is my call to my preload function. This is a very simple call to a php file that simply goes and looks for the next set of images in order, if there is any to find. If it gets some images then it begins adding it to a temporary div in memory (again not on the DOM to be seen) and is now setup for simple DOM traversal. Let's view the function to dissect its functionality...
function preLoadNextImgSet() {
$.post('AjaxController/ajaxPreload_Gallery.php', {currStart: start, currSize: loadSize}, function(data) {
if(data!="") {
var y = $(document.createElement("div")).append(data).find("a"),
found = [];
y.each(function() {
found[found.length] = "img/gallery/" + $(this).text();
});
$.preloadImages(found);
}
});
} //end function
In this example I have two global variables living in my browser window from JavaScript that I would have declared. A start and a loadSize variable. The start variable represents the current place in our list of images that we currently are at and the loadSize variable sets a limit on how many images to preload each time.
Now that the variables are set and sent in to the PHP file via the $.post function, we can use the PHP file to find the appropriate images in order and have them loaded into memory awaiting usage. Whatever is returned here to the y variable gets iterated over by the each function and then preloaded. Once this functions scope is exited the imaginary div will be deleted and sent to garbage as it is not used simple iterated over.
Ok, now. Its been a journey but we are almost ready to begin the final method here. Let's first go back and look at what the first imagesLoaded call was doing now that we know the new functionality added in these functions. The imagesLoaded call in the DOM-Ready event has a call in its very bottom piece that preloads the images.... why? This is because once the page loads and the initial images are loaded into the isotope container, we need the page to now use this idle time to begin already loading the next set. So in other words once the images are placed and sorted and happy to just sit there, the next loadSize amount of images will be loaded and waiting for you to place them.
Now for the final function. This function is a generic function thats sole purpose is to load in the current preloaded images into the DOM officially and then to ask for the next set to be loaded. However what on earth would be calling this function? This is where the lazyloading or infinitescroll becomes useful to us. Somewhere in your page you need to add this function in...
$(window).scroll(function(){
scrollTop = $(window).scrollTop(),
windowHeight = $(window).height(),
docuHeight = $(document).height();
//AJAX Data Pull
if(((scrollTop + windowHeight)+35) >= docuHeight){
getNextImages();
}
});
This function is the magic function that allows the infinitescroll effect to occur. I have added 35 pixels or so of padding (the +35 randomly in my code) because sometimes you want it to load close to the end of the page but not quite the actual end of the page.
Ok so now that this is setup when we reach the end of the page this function will want to get all of the next images generically like we had mentioned. The function of mine looks like this...
function getNextImages() {
cont = $(container);
$.post('AjaxController/ajaxPortfolio_Gallery.php', {currStart: start, currSize: loadSize}, function(data) {
if(data!="") {
//Append New Photos Inside <a> Element Tag
var y = $(document.createElement("div")).append(data).find("a");
cont.append(y);
//Fix Image Layouts
cont.imagesLoaded(function() {
//Feed Isotope Layout The New Items
cont.isotope("appended", y);
cont.find("a").css({"opacity":"1"});
});
} else { unFilled = false; }
});
}
I have included the unFilled variable simply so that there is a flag that can be set when you have reached the end of the images. You don't want it to keep trying to load forever if there are no images left to show.
Ok, so. This is a lot of information so I will try to keep answering as much as possible.

Ensure knockout binding gets processed first

I have a table created from an observable array. Table rows contains elements belonging each to one of a set of categories. To filter the table based on categories, there is a row of buttons.
Buttons can be active or inactive, indicated via a CSS class bound via knockout:
<button data-bind="click: filter.filterCategory, css: { filterOn: filter.category.isFiltered() }">Filter</button>
Filtering within the table is done by switching the display state of rows:
<tr data-bind="css: { nonDisplay: !table.category.isDisplayed() }">
</tr>
The click handler mainly sets the values of the two observables, in sequence e.g.
vm.filter.category.isFiltered(true);
vm.table.category.isDisplayed(false);
This works in principle.
The problem is that the indication that the filter button has been selected by the user is not given immediately, but dependent on the execution time of the filtering itself, i.e. the changes to the table.
With larger tables, and especially on mobile, this can mean delays of a couple of seconds.
I can live with the filtering itself taking this long, but the feedback needs to be immediate.
Is there a way to ensure that the change on vm.filter.category.isFiltered gets applied before the longer running change based on vm.table.category.isDisplayed is started?
This seems as an async fail.
You should implement a callback method parameter in the isFiltered method, something like this
var vm = vm || {};
vm.filter = vm.filter || {};
vm.filter.category = (function($){
var that = this;
that.isFiltered = function(booleanValue, callback) {
// Put your primary code here
console.log("this runs first");
// ...when the first code is done
callback();
};
that.isDisplayed = function(booleanValue) {
console.log("this runs second");
};
return that;
})($);
// Implement by stating a method as the second parameter.
vm.filter.category.isFiltered(true, function(){ vm.filter.category.isDisplayed(false); });
This will render
// this runs first
// this runs second

How do I set up sequences of functions that I want executed in JavaScript?

I'm working on a JavaScript driven site where I will have a lot of stuff that need's to be executed in a certain order. A lot of the stuff involves animations and AJAX-loading. Some pseudo code could look like this:
Load JSON formated data
Generate HTML-elements using the loaded JSON data and render them inside a div
Make the elements inside the div scrollable using a jQuery UI slider
Randomize a number between 1 and the total number of loaded elements
Make the jQuery UI slider scroll (animate) to the element that represents the randomized number for a duration of 500 milliseconds
Load more JSON formated data
Replace other elements on the page
And so on...
Each step in this is wrapped in a function - one function loads the JSON data, another generates the HTML-elements, a third initializes the jQuery UI slider and so on. Encapsulating the code in functions makes the code easier to read for me, but above all I want to be able to call the functions in different orders depending on what happens on the page and I want to be sure that one function has finished running before the next one is executed.
If there was just regular functions that didn't involve AJAX or jQuery animations I'd just execute the functions I want to execute, one after the other. The problem is that I need to wait for the animations and data retrieving functions to finish before moving on. To aid me both the animation and AJAX methods in jQuery allow me to send along a callback. But here's where I get lost.
What I want it to do is the following:
Load JSON data. If the loading is successful, go on and...
Generate HTML-elements
Make the elements scrollble
Randomize a number between 1 and the total number of loaded elements and pass it to...
A function that makes the jQuery slider slide (animated) to the element. When the animation is finished...
Load more JSON formated data. If the loading is successful, go on and...
Replace other elements on the page
The ideal thing would be if I could set up this sequence/chain of events in one single place, for example inside an event handler. If I want to call the functions in a different order or not call all of them I would just set up a different sequence/chain. An example could be:
Randomize a number between 1 and the total number of loaded elements and pass it to...
A function that makes the jQuery slider slide (animated) to the element. When the animation is finished...
This means that I'd have to be in control over the callbacks in each step.
I hope you understand what I'm looking for. I want to control the entire execution sequence from a single function. This function would be "the conductor of the orchestra" and all the other functions would be the different instrument sections of the orchestra. This conductor functions need's ears so it can hear when the violinist is finished with her solo and can tell the horns to start playing. Excuse me for the corny allegory, but I hopes it makes it easier to understand what I want to do.
Thanks in advance!
/Thomas
Would the jQuery .queue() function help you?
Could you store a sequencer variable that is an array (which you would be able to change) and then call a sequencer at the end of each function?
You could then pass a step code through each function and cross-reference that with the sequencer variable as to what the next step should be.
Pseudo Code:
var func['1'] = function(arg1,arg2,seq) {
//code
sequencer(seq);
}
var func['2'] = function(arg1,arg2,seq) {
//code
sequencer(seq);
}
var func['3'] = function(arg1,arg2,seq) {
//code
sequencer(seq);
}
var func['4'] = function(arg1,arg2,seq) {
//code
sequencer(seq);
}
function sequencer(seq) {
seq = seq + 1;
window.func[seq]
}
I tried executing this code:
var seq = 0;
var func = [];
func[1] = function(seq) {
setTimeout(function() {
console.log("Executing function 1");
sequencer(seq);
}, 2000);
}
func[2] = function(seq) {
console.log("Executing function 2");
sequencer(seq);
}
func[3] = function(seq) {
console.log("Executing function 3");
}
function sequencer(seq) {
seq = seq + 1;
func[seq].call();
}
sequencer(seq);
But the result (in Firebug) is:
Executing function 1
func[seq] is undefined
[Break on this error] func[seq].call();
I think that the problem is caused by context, but I'm not sure. JavaScript is sensitive to the context in which a function is called.
/Thomas
I found that what I was trying to achieve was slightly overkill for my purposes. So I decided to go with a different approach. I can send one or more boolean variables as a parameters to a function and use them to decide whether to execute a second function or not. Here's an example:
$("#justOneStep").click(function() {
loadImage(false);
});
$("#twoStepsPlease").click(function() {
loadImage(true);
});
function loadImage(boolDoMore) {
// Do the stuff that loads an image
...
if(boolDoMore) {
nextFunction();
}
}
function nextFunction() {
// Do some more stuff
...
}
Not very fancy but easy to understand and control and sufficient for my needs at the moment.
/Thomas

Categories

Resources