I am using express to show a list of running processes. I want them to reload every few seconds to remove those that are finished and add new ones.
I am using ajax for this:
My layout.pug (abridged)
div(class="processes")
for process in processes
div(class="process")=process.name
script.
setInterval(() => {
$.ajax({
url: "/processes/running",
type: "GET",
success: function(response, status, http) {
if (response) {
processes = $('.processes')[0]
html = ""
for(process of response){
html +='<div class="process">'+process.name+'</div>'
}
$(processes).html(html)
}
}
});
}, 3000)
And the corresponding route is just
app.get('/processes/running', function(req, res){
res.send(processes)
})
where processes is the array I use to keep track of them.
This works fine. However, I don't like the code duplication. I basically have to build the list/table two times in my code. If I change something, I would have to change it in both places...
So, is there some better way to accomplish this?
A solution to avoir duplication is simply to not initialise the inside of you div yet
div(class="processes")
script.
setInterval(() => {
$.ajax({
url: "/processes/running",
type: "GET",
success: function(response, status, http) {
if (response) {
processes = $('.processes')[0]
html = ""
for(process of response){
html +='<div class="process">'+process.name+'</div>'
}
$(processes).html(html)
}
}
});
}, 3000)
The div will be populated after the first call.
If you have already data you could put the "html creation part" (the if(response) block) in a function and call it before calling setInterval and in the response as now.
This will allow you to have the code that define your array in a single place
If you don't have yet data, I suggest to show a message in the div like "No data yet please wait"
Related
I have a key_gen.php file that contains a function to generate a random key. When executed, the php file gives back one key (tested and it works).
In my javascript file I have a click event on a button (that works), something like this:
$('#kg_btn').click(function(){});
Then, inside my click event I have a functions:
var get_key = function(){
for(var i = 0; i < kg_quantity; i++) {
$.ajax ({
url: "../keygen/gen_rkey.php",
type: "GET",
datatype: "json",
success: function(data) {
var j_obj = JSON.parse(data);
//alert("Success!");
prs = j_obj.test;
console.log(prs);
//add_key();
},
error: function(error) {
alert("Error! Can't send the data");
}
}); //ajax call ends
}
}
When I run this function once (by setting up the "Kg_quantity" variable to 1), every time I click my button I get a correct behavior. The result is a different key on the console.log per click.
If I set up the "kg_quantity" to any other number than 1 (for example: 3,9,10), I do get the console.log messages back, but the number generated is the same.
I was hoping that by inserting the ajax object into a for-loop would execute the ajax call several times. I tried to put the ajax call within a prototype function, as well, but I get the same result.
Edit: I tried adding a closure (as Ross suggested), but I get the exact same result.
Thanks.
AJAX is asynchronous. Your loop will finish before the first AJAX response most likely.
I would restructure that so the success response from the AJAX call will trigger the next iteration. Or if you're lazy you can just set async:false:
$.ajax ({
url: "../keygen/gen_rkey.php",
type: "GET",
async: false,
....
Or even better, put the strain on the server and reduce the back-and-forth so you get all your keys in one response:
url: "../keygen/gen_rkey.php?qty=" + kg_quantity,
UPDATE: Async design method:
function getKeys(max,cur) {
cur = cur || 1;
if (cur == max) {
return;
}
$.ajax({
....
success(function(data) {
// do stuff....
// recursive iteration
getKeys(max,cur + 1);
}
});
}
getKeys(5);
I've got a dashboard page, and am using jQuery to update each graph with a single ajax call.
If it run AJAX with async:false then everything works, but it's obviously slow as the calls are made one after another.
When I run async:true, the queries execute but they all output to the same element and overwrite each other.
How can I ensure that the jQuery selector in the success and error functions remain pointed to their original desintation and do not all point to the final box?
My code:
//update sparklines on dashboard page
$(".convobox7").each(function() {
id = $(this).attr('id');
$("#convobox-7-"+id).prepend("<img src='img/ajax_loader.gif'/>");
$.ajaxQueue({
url: '_ajax/getFunnelReport',
type: "POST",
dataType: "json",
async: true,
data: {funnel:$(this).attr('id'), dimension:'date'},
timeout: 50000,
success: function(json) {
var data = json;
if (data.success=='true') {
$("#convobox-7-"+id).html(data.htmlconv+"<br/><small>Past week</small>");
gebo_peity.init();
}
},
error: function(x, t, m) {
$("#convobox-7-"+id).html("");
}
})
});
Note I'm using the ajaxQueue plugin here but the same thing happens without it.
You need to localise id :
var id = $(this).attr('id');
There may be other things to fix but that one is a certainty.
EDIT
Try this :
$(".convobox7").each(function() {
var id = $(this).attr('id');
var $el = $("#convobox-7-"+id).prepend("<img src='img/ajax_loader.gif'/>");
$.ajaxQueue({
url: '_ajax/getFunnelReport',
type: "POST",
dataType: "json",
data: {funnel:id, dimension:'date'},
timeout: 50000,
success: function(data) {
if (data.success == 'true') {
$el.html(data.htmlconv+"<br/><small>Past week</small>");
gebo_peity.init();
}
},
error: function(x, t, m) {
$el.html("");
}
});
});
This has to do with function closures because you declared the variable outside the success/error function. A better approach is to use the $(this) reference in the error/success functions instead of assigning it outside the handlers.
Edit: In the context of the error/success handler for ajaxQueue, I'm not absolutely certain what $(this) refers to, you may need to navigate to a parent element. I didn't see any definitive documentation offhand. This is one of my biggest pet peeves with javascript documentation, $(this) is sometimes not what you would think it'd be and isn't documented :/
silly question, but since you already send the element id to the service, is there a reason it cannot send it back? then you can simply use that as a selector, ensuring that you have the item you need.
I'm attempting to pull two separate things from outside sources to put onto an HTML page I'm creating. I have a successful AJAX function to pull the most recent video from a particular Youtube channel by parsing through the XML/RSS feed for that channel. I receive this feed through an AJAX call.
I'd also like to get the most recent blog post from a Blogger account. The code for parsing the feed to get the most recent entry shouldn't be difficult, but I'm having trouble with simultaneous AJAX calls. I read somewhere that it can only handle one at a time? I'm weary about queuing them because I don't want to the content on the page to load in steps. I'd rather it all just get fetched simultaneously. How might I go about doing this?
Here is my current script:
<script type="text/javascript" charset="utf-8">
$(function() {
$.ajax({
type: "GET",
url: "http://gdata.youtube.com/feeds/base/users/devinsupertramp/uploads?orderby=updated&alt=rss&client=ytapi-youtube-rss-redirect&v=2",
dataType: "xml",
success: parseXml
});
});
function parseXml(xml) {
$(xml).find("item:first").each(
function() {
var tmp = $(this).find("link:first").text();
tmp = tmp.replace("http://www.youtube.com/watch?v=", "");
tmp = tmp.replace("&feature=youtube_gdata", "");
var tmp2 = "http://www.youtube.com/embed/" + tmp + "?autoplay=1&controls=0&rel=0&showinfo=0&autohide=1";
var iframe = $("#ytplayer");
$(iframe).attr('src', tmp2);
}
);
}
</script>
I read somewhere that it can only handle one at a time?
Either you misunderstood what the person was trying to say or they were incorrect. Javascript doesn't run any functions concurrently so someone with poor English might reword that as "can only handle one at a time" but that doesn't mean you can't make multiple AJAX calls. jQuery is smart and will do what it needs to do to make sure both calls are executed eventually.
If you'd like all the content to be loaded simultaneously the sad fact is you can't. However you can make it appear that way to the user by declaring a flag that is set by the success method of each call. Then just keep the content hidden until both flags have been set.
EDIT:
Here's a very simplistic approach to make it appear that they are fetched simultaneously:
<script type="text/javascript" charset="utf-8">
var youtubComplete = false;
var otherComplete = false;
$(function() {
$.ajax({
type: "GET",
url: "http://gdata.youtube.com/feeds/base/users/devinsupertramp/uploads?orderby=updated&alt=rss&client=ytapi-youtube-rss-redirect&v=2",
dataType: "xml",
success: parseXml
});
$.ajax({
type: "GET",
url: "http://someotherdata.com/",
dataType: "xml",
success: function() { otherComplete = true; checkFinished(); }
});
});
function parseXml(xml) {
$(xml).find("item:first").each(
function() {
var tmp = $(this).find("link:first").text();
tmp = tmp.replace("http://www.youtube.com/watch?v=", "");
tmp = tmp.replace("&feature=youtube_gdata", "");
var tmp2 = "http://www.youtube.com/embed/" + tmp + "?autoplay=1&controls=0&rel=0&showinfo=0&autohide=1";
var iframe = $("#ytplayer");
$(iframe).attr('src', tmp2);
}
);
youtubeComplete = true;
checkFinished();
}
function checkFinished()
{
if(!youtubeComplete || !otherComplete) return;
// ... Unhide your content.
}
</script>
The browser will support multiple outbound calls but there is a cap per domain. Take a look at this related Q/A How many concurrent AJAX (XmlHttpRequest) requests are allowed in popular browsers?.
There are several good libraries for doing request scheduling including chaining and parallelizing AJAX calls. One good library is https://github.com/kriskowal/q, which provides async promises framework to enable arbitrarily complicated chaining of AJAX requests. Q minified is about 3.3KB.
// The jQuery.ajax function returns a 'then' able
Q.when($.ajax(url, {dataType: "xml"}))
.then(function (data) {
var parsedXML = parseXML(data)
...
// some other ajax call
var urls = [Q.when($.ajax(url2, {data: {user: data.userId}})),
Q.when($.ajax(url3, {data: {user: data.userId}}))];
// run in parallel
return Q.all(urls)
})
.then(function (data) {
// data retrieved from url2, url2
})
I need to retrieve data via cross-domain XMLHttpRequest. To make this work in (almost) all browsers, I use native XHR first and, if that fails, flXHR.
The (working) code I currently have for this is as follows:
jQuery.support.cors = true; // must set this for IE to work
$.ajax({
url: 'http://site.com/dataToGet',
transport : 'xhr',
success: function(data) {
console.log('Got data via XHR');
doStuff(data);
},
error: function(xhr, textStatus, error) {
console.log('Error in xhr:', error.message);
console.log('Trying flXHR...');
$.ajax({
url: 'http://site.com/dataToGet',
transport : 'flXHRproxy',
success: function (data) {
console.log('Got data via flXHR');
doStuff(data);
},
error: function (xhr, textStatus, error) {
console.log('Error in flXHR:', error.message);
console.log('Both methods failed, data not retrieved.');
}
});
}
});
This feels like a lot of code duplication to me, especially in the success handlers. Is there a more efficient way to do this? I'd really prefer to make one $.ajax call that would try both transports in turn, instead of having to use the error handler to make the call a second time. It's not too bad in this example, but rapidly gets more complicated if the success handler is longer or if the success handler has to itself issue another $.ajax call.
I've created a jquery-specific and slimmed-down fork of flxhr that simplifies your code sample above. You can see an example of usage in the "Usage" section in the README.
https://github.com/b9chris/flxhr-jquery-packed
In particular, you don't want to waste time waiting for a standard CORS request to fail. It's easy to determine whether flxhr is necessary by testing $.support.cors upfront (no need to override it). Then just use flxhr explicitly where necessary.
Why don't you just wrap this in a function by itself? That's after all, how you end up reusing code. You can even pass functions as arguments to make sure that you don't have to repeat this code more than once.
To me this is pretty straight forward but maybe I've misunderstood.
function xhr(success) {
$.ajax({
success: success,
error: function() {
$.ajax({ success: success })
}
});
}
Then just pass the success handler once
xhr(function(data){/*magic*/});
Or if you wanna basically avoid redundant configuration of the ajax call use the first object as a template, like this:
function xhr(success) {
var ajaxParams = { success: success };
ajaxParams.error = function() {
$.ajax($.extend(ajaxParams, { transport: 'xhr' }));
}
$.ajax(ajaxParams);
}
I simplified the whole thing a bit, but I hope you get the point.
Edit
Reading that last bit, maybe this will give you some ideas... it's a variation of that last snippet.
function xhr(success) {
var ajaxParams = { success: success };
ajaxParams.error = function() {
var newParams = $.extend(ajaxParams, { transport: 'xhr' });
newParams.success = function() {
// do something
// arguments is a special array, even if no parameters were
// defined in any arguments where passed they will be found
// in the order they were passed in the arguments array
// this makes it possible to forward the call to another
// function
success.apply(this, arguments);
}
$.ajax(newParams);
}
$.ajax(ajaxParams);
}
Hey. I need some help with jQuery Ajax calls. In javascript I have to generste ajax calls to the controller, which retrieves a value from the model. I am then checking the value that is returned and making further ajax calls if necessary, say if the value reaches a particular threshold I can stop the ajax calls.
This requires ajax calls that need to be processes one after the other. I tried using async:false, but it freezes up the browser and any jQuery changes i make at the frontend are not reflected. Is there any way around this??
Thanks in advance.
You should make the next ajax call after the first one has finished like this for example:
function getResult(value) {
$.ajax({
url: 'server/url',
data: { value: value },
success: function(data) {
getResult(data.newValue);
}
});
}
I used array of steps and callback function to continue executing where async started. Works perfect for me.
var tasks = [];
for(i=0;i<20;i++){
tasks.push(i); //can be replaced with list of steps, url and so on
}
var current = 0;
function doAjax(callback) {
//check to make sure there are more requests to make
if (current < tasks.length -1 ) {
var uploadURL ="http://localhost/someSequentialToDo";
//and
var myData = tasks[current];
current++;
//make the AJAX request with the given data
$.ajax({
type: 'GET',
url : uploadURL,
data: {index: current},
dataType : 'json',
success : function (serverResponse) {
doAjax(callback);
}
});
}
else
{
callback();
console.log("this is end");
}
}
function sth(){
var datum = Date();
doAjax( function(){
console.log(datum); //displays time when ajax started
console.log(Date()); //when ajax finished
});
}
console.log("start");
sth();
In the success callback function, just make another $.ajax request if necessary. (Setting async: false causes the browser to run the request as the same thread as everything else; that's why it freezes up.)
Use a callback function, there are two: success and error.
From the jQuery ajax page:
$.ajax({
url: "test.html",
context: document.body,
success: function(){
// Do processing, call function for next ajax
}
});
A (very) simplified example:
function doAjax() {
// get url and parameters
var myurl = /* somethingsomething */;
$.ajax({
url: myurl,
context: document.body,
success: function(data){
if(data < threshold) {
doAjax();
}
}
});
}
Try using $.when() (available since 1.5) you can have a single callback that triggers once all calls are made, its cleaner and much more elegant. It ends up looking something like this:
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
// a1 and a2 are arguments resolved for the page1 and page2 ajax requests, respectively
var jqXHR = a1[2]; /* arguments are [ "success", statusText, jqXHR ] */
alert( jqXHR.responseText )
});