I need to add a function to my script that gets the current server time.
I use the PHP file below to get the server time in milliseconds.
<?php
date_default_timezone_set('Europe/London');
$serverTime = round(microtime(true) * 1000);
echo json_encode($serverTime);
?>
Then i would like to add an Ajax request to 'get' serverTime.PHP and put it into a variable so that i can calculate the time before something ends correctly.
I currently get the clients time by using this
var now = new Date ().getTime();
Now i want to remove that line and add my ajax request.
I have tried adding the following code into the script but i can not get it to execute
function now ()
{
this.ajax.open('GET', 'serverTime.php',
true);
this.ajax.send(null);
if (this.ajax.readyState != 4) return;
if (this.ajax.status == 200)
{
// get response
var now = eval ('('+this.ajax.responseText+')');
}
}
The end result being the variable 'NOW' contains the output of serverTime.PHP
Here is my script, i have tried to add anothert ajax get request in various ways but i cant get it to function correctly.
$.ajaxSetup({
type: 'GET',
headers: { "cache-control" : "no-cache" }
});
var PlayList = function (onUpdate, onError)
{
// store user callbacks
this.onUpdate = onUpdate;
this.onError = onError;
// setup internal event handlers
this.onSongEnd = onSongEnd.bind (this);
// allocate an Ajax handler
try
{
this.ajax = window.XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e)
{
// fatal error: could not get an Ajax handler
this.onError ("could not allocated Ajax handler");
}
this.ajax.onreadystatechange = onAjaxUpdate.bind(this);
// launch initial request
this.onSongEnd ();
// ------------------------------------------
// interface
// ------------------------------------------
// try another refresh in the specified amount of seconds
this.retry = function (delay)
{
setTimeout (this.onSongEnd, delay*5000);
}
// ------------------------------------------
// ancillary functions
// ------------------------------------------
// called when it's time to refresh the playlist
function onSongEnd ()
{
// ask for a playlist update
this.ajax.open('GET', 'playlist.php', // <-- reference your PHP script here
true);
this.ajax.send(null);
}
// called to handle Ajax request progress
function onAjaxUpdate ()
{
if (this.ajax.readyState != 4) return;
if (this.ajax.status == 200)
{
// get response
var list = eval ('('+this.ajax.responseText+')');
// compute milliseconds remaining till the end of the current song
var start = new Date(list[0].date_played.replace(' ', 'T')).getTime();
var now = new Date ( ).getTime();
var d = start - now + 6500
+ parseInt(list[0].duration);
if (d < 0)
{
// no new song started, retry in 3 seconds
d = 3000;
}
else
{
// notify caller
this.onUpdate (list);
}
// schedule next refresh
setTimeout (this.onSongEnd, d);
}
else
{
// Ajax request failed. Most likely a fatal error
this.onError ("Ajax request failed");
}
}
}
var list = new PlayList (playlistupdate, playlisterror);
function playlistupdate (list)
{
for (var i = 0 ; i != list.length ; i++)
{
var song = list[i];
}
{
document.getElementById("list0artist").innerHTML=list[0].artist;
document.getElementById("list0title").innerHTML=list[0].title;
document.getElementById("list0label").innerHTML=list[0].label;
document.getElementById("list0albumyear").innerHTML=list[0].albumyear;
document.getElementById("list0picture").innerHTML='<img src="/testsite/covers/' + list[0].picture + '" width="170" height="170"/>';
document.getElementById("list1artist").innerHTML=list[1].artist;
document.getElementById("list1title").innerHTML=list[1].title;
document.getElementById("list1label").innerHTML=list[1].label;
document.getElementById("list1albumyear").innerHTML=list[1].albumyear;
document.getElementById("list1picture").innerHTML='<img src="/testsite/covers/' + list[1].picture + '" width="84" height="84"/>';
document.getElementById("list2artist").innerHTML=list[2].artist;
document.getElementById("list2title").innerHTML=list[2].title;
document.getElementById("list2label").innerHTML=list[2].label;
document.getElementById("list2albumyear").innerHTML=list[2].albumyear;
document.getElementById("list2picture").innerHTML='<img src="/testsite/covers/' + list[2].picture + '" width="84" height="84"/>';
document.getElementById("list3artist").innerHTML=list[3].artist;
document.getElementById("list3title").innerHTML=list[3].title;
document.getElementById("list3label").innerHTML=list[3].label;
document.getElementById("list3albumyear").innerHTML=list[3].albumyear;
document.getElementById("list3picture").innerHTML='<img src="/testsite/covers/' + list[3].picture + '" width="84" height="84"/>';
document.getElementById("list4artist").innerHTML=list[4].artist;
document.getElementById("list4title").innerHTML=list[4].title;
document.getElementById("list4label").innerHTML=list[4].label;
document.getElementById("list4albumyear").innerHTML=list[4].albumyear;
document.getElementById("list4picture").innerHTML='<img src="/testsite/covers/' + list[4].picture + '" width="84" height="84"/>';
$('.nowPlayNoAnimate').each(function() {
$(this).toggleClass('nowPlayAnimate', $(this).parent().width() < $(this).width());
});
}
}
function playlisterror (msg)
{
// display error message
console.log ("Ajax error: "+msg);
//retry
list.retry (10); // retry in 10 seconds
}
Why not use the jquery method ?
function getServerTime() {
var now = null;
$.ajax({
url: "serverTime.php",
dataType: 'JSON',
async: false,
success: function (data) {
now = data;
}
});
return now;
}
You can start as many requests as you want in a browser-portable way.
PS:
you may also want to replace
document.getElementById("list4artist").innerHTML=list[4].artist;
by the shorter
$("#list4artist").html(list[4].artist);
Edit: Add async parameter to make the execution wait for the ajax call to simulate a non-asynchronous function call.
Assuming your service return the date object, you need to add the following line inside the success function (suggested by Clément Prévost):
var now = data;
The success function is an async callback function the triggers after the server return a value.
You should read about Jquery Ajax, it will make your life much easier.
Related
I have the following function in the client-side of a web app:
function fetchDataFromApi(fetchCode, options, callback) {
var dataObject = JSON;
dataObject.fetchCode = fetchCode;
dataObject.options = options;
var xhr = new XMLHttpRequest();
var url = "DATA_API_URL";
// connect to the API
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type",
"application/json"
);
// set callback for when API responds. This will be called once the request is answered by the API.
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
// API has responded;
var json = {
ok: false,
message: 'could not parse response'
};
try {
// parse the raw response into the API response object
json = JSON.parse(xhr.responseText);
} catch (err) {
// probably json parse error; show raw response and error message
console.log(err);
console.log("raw response: " + xhr.responseText);
}
if (json.ok) {
// success, execute callback with argument json.data
callback(json.data);
} else {
// fetch failed;
console.error(json.message);
}
}
};
// send request payload to API
var data = JSON.stringify(dataObject);
xhr.send(data);
}
Since I am using an asynchronous call (the third parameter in xhr.open is set to true), I am surprised to find that this function blocks the UI in the browser. When there is a substantial amount of data grabbed from the server with this function, it can take 3-4 seconds, blocking the UI and generating this error in the Chrome console:
[Violation] 'load' handler took 3340ms
This function is currently in production here, where I am calling the function as so:
function getNamesFromApi() {
fetchDataFromApi('chj-confraternity-list', {}, function (data) {
fadeReplace(document.getElementById('spinner-2'), document.getElementById(
'name-list-container'),
false, true);
// transaction was successful; display names
var listString = "";
if (data.list) {
// add the names to the page
var listLength = data.list.length;
for (var x = 0; x < listLength; x++) {
document.getElementById('name-list-container').innerHTML +=
"<div class='name-list-item'>" +
"<span class='name-list-name'>" +
data.list[x].name +
"</span>" +
"<span class='name-list-location'>" +
data.list[x].location +
"</span>" +
"</div>";
}
}
});
}
window.addEventListener('load', function() {
getNamesFromApi();
});
Why is this blocking the UI, and what am I doing wrong in making an asynchronous XMLHttpRequest?
UPDATE: Thanks to the comments for pointing me in the right direction; the issue was not the XMLHttpRequest, but rather me appending innerHTMl within a loop. The issue is now fixed, with the corrected snippet in the answer.
The UI was blocked because i was appending innerHTML within a loop, an expensive, and UI-blocking operation. The issue is now fixed. Here is the corrected snippet:
function getNamesFromApi() {
fetchDataFromApi('chj-confraternity-list', {}, function (data) {
fadeReplace(document.getElementById('spinner-2'), document.getElementById(
'name-list-container'),
false, true);
// transaction was successful; display names
if (data.list) {
var listString = "";
// add the names to the page
var listLength = data.list.length;
for (var x = 0; x < listLength; x++) {
listString +=
"<div class='name-list-item'>" +
"<span class='name-list-name'>" +
data.list[x].name +
"</span>" +
"<span class='name-list-location'>" +
data.list[x].location +
"</span>" +
"</div>";
}
document.getElementById('name-list-container').innerHTML = listString;
}
});
}
window.addEventListener('load', function() {
getNamesFromApi();
});
I have a JavaScript client that works in Chrome and Firefox, but fails in IE. Looking at the network trace in the IE debugger it shows that multiple of the AJAX calls have been aborted.
I've been able to get around it by setting the timeout to 0. I'd like to know if this is the correct way to handle my requests being aborted? Basically what could go wrong?
My initial thought was that I should capture and resend on error, and if multiple resubmits do not result in a completed request, finally alert the user. I'd still like to know how to do this even if the setTimeout is the proper way to address my immediate issue.
Also the application will process an excel workbook of addresses, call a web service to add some data to them and then allow the user to download the enhanced file.
This is what I have so far, first in the app.js
var requestWithFeedback = function (args) {
$(".loader").removeClass('hidden');
var oldConfig = args.config || function () { };
args.config = function (xhr) {
xhr.setRequestHeader("Authorization", "Bearer " + localStorage.token);
oldConfig(xhr);
extract: extract;
};
var deferred = m.deferred();
setTimeout(function () { // <== This solved in IE, but is this the way to handle this?
m.request(args).then(deferred.resolve, function(err){
if (err === "Invalid token!"){
m.route('/');
}
})}, 0);
$(".loader").addClass('hidden');
return deferred.promise;
}
From the model.js
app.MarkedAddresses.ProcessAddressBatch = function () {
var requestData = {
Addresses: app.MarkedAddresses.vm.addresses
}
return requestWithFeedback({
method: "POST"
, url: "API/server.ashx"
, data: requestData
, deserialize: function (value) { return value; }
})
.then(function (value) {
var responseJSON = $.parseJSON(value);
$.merge(app.MarkedAddresses.vm.results, responseJSON)
app.MarkedAddresses.vm.currentRecord(app.MarkedAddresses.vm.results.length);
app.MarkedAddresses.vm.progress(Math.max(app.MarkedAddresses.vm.progress(), ~~(app.MarkedAddresses.vm.currentRecord() / app.MarkedAddresses.vm.totalRecords() * 100)));
m.redraw(); //Force redraw for progress bar
return value;
},
function (error) { console.log(error) } // <== I thought error would show up here, but I never hit a breakpoint here.
);
}
Added loops
function process_wb(wb) {
app.MarkedAddresses.vm.results.length = 0;
$('.descending').removeClass("descending");
$('.ascending').removeClass("ascending");
app.MarkedAddresses.vm.progress(.1);
m.redraw();
var header = mapHeader(wb);
var addressJSON = to_json(wb, header);
app.MarkedAddresses.vm.totalRecords(addressJSON.length);
for (var i = 0; (i < addressJSON.length + 1) ; i += 1000) {
app.MarkedAddresses.vm.addresses = addressJSON.slice(i, Math.min(((i) + 1000), addressJSON.length));
app.MarkedAddresses.vm.response(new app.MarkedAddresses.vm.processAddressBatch());
}
}
Why isn't the error triggered in the section of the code?
It seems like I should add a deferred section here, but anything I've tried has been a syntax error.
I am having trouble with ajax/promises. I have two ajax requests total, with the second ajax call relying data to be returned by the first ajax call.
My first ajax call finds Latitude, Longitude, and country code of the value of #search.
My second ajax call finds the weather of that city, but the API URL is dependent on the Latitude, Longitude and country code that my first ajax call returns. So the second ajax call can't be started until the first one is finished.
My logic here is that var ajax1 is assigned a promise, and var ajax2 starts after ajax1.then() checks that ajax1's promise is resolved. Then ajax2 runs and returns another promise. Finally ajax2.done starts after it checks that ajax2's promise is resolved, and then starting my successWeatherFunction().
My problem is that ajax2.done is not working, as the console.log("test") is not showing up on the console. The two earlier console.logs, console.log(info) and console.log(weatherApiUrl) are working.
Thanks!
$("#search").keypress(function(event) {
if (event.which === 13) {
var searchCity = $("#search").val();
var jsonURL = "http://autocomplete.wunderground.com/aq?query=" + searchCity + "&cb=?"
var ajax1 = $.getJSON(jsonURL);
var ajax2 = ajax1.then(function(data) {
var info = [];
info.push(data["RESULTS"][0]["name"]);
info.push(data["RESULTS"][0]["c"]);
info.push(data["RESULTS"][0]["lat"]);
info.push(data["RESULTS"][0]["lon"]);
console.log(info);
var searchLat = info[2];
var searchLng = info[3];
var countryCode = info[1];
if (countryCode === "US") {
var weatherApiUrl = "https://api.forecast.io/forecast/{APIKEY}/" + searchLat + "," + searchLng + "?exclude=minutely" + "&callback=?";
} else {
var weatherApiUrl = "https://api.forecast.io/forecast/{APIKEY}/" + searchLat + "," + searchLng + "?exclude=minutely" + "?units=si" + "&callback=?";
console.log(weatherApiUrl);
}
return $.getJSON(weatherApiUrl);
});
ajax2.done(function(data){
console.log("test");
successCityWeather(data);
});
Your code use then and done. done is the old promises jQuery syntax so you should use only then.
The following code works for me :
$(function() {
$.get('/test').then(function() {
console.log('First request end');
return $.get('/test');
}).then(function() {
console.log('second request end');
});
});
But in your case, maybe a one of your request fail. Give a second parameter to then to log the error :
$.getJSON('...').then(function(data) {
console.log('success', data);
}, function(data) {
console.log('fail', data);
});
If not sure, always use always() handler. That way you will know if the request actually finished with error or not at all.
$.ajax( ...params... )
.always(function(jqXHR, textStatus) {
if (textStatus != "success") {
alert("Error: " + jqXHR.statusText); //error is always called .statusText
} else {
alert("Success: " + jqXHR.response); //might not always be named .response
}});
$.post(jsonURL)
.then(function (data) {
var info = [];
// some actions
return $.getJSON(weatherApiUrl);
})
.then(function(data, status, promise) {
// some actions
successCityWeather(data);
})
I have a javaScript object having some value. I need to call a function as many times as the object has the value. Look at the code snipet
for(var i=0; i < scroll["altscroll"]; i++){
more_alt_leftajaxsearchcategory(0, 0, element=false, back = true);
}
The called function loads some element through ajax method and append it to the body.
The problem is that when loop executes the function is called instantaneously but the called function returned result not in the order expected. For example if function is called 3 times and if returned result in last calling is smaller in size then it appends first in the body after that the other returned result is appended.
Can we serialize the function call so that it does not call another function until the first one completes.
The called function code snipet:
var pageCount = 1;
function more_leftajaxsearchcategory(locval,pageid, element, back){
var offsetCount = pageCount * 10;
var dataString = jQuery("#leftformid").serialize();
jQuery.ajax({
url:"<?php echo admin_url('admin-ajax.php'); ?>",
type:'POST',
data: 'action=leftmenusearchcategory'+'&locationval='+locval+'&numb='+offsetCount+'&'+dataString,
beforeSend: function(){jQuery('body').append('<p class="overBck"><img alt="Loading..." src="<?php bloginfo('template_url'); ?>/images/ajax-loader.gif" style="margin-top:25%;"></p>'); },
success:function(results, status)
{
jQuery(".book_results").append(results);
if(results == 'Sorry no results found. Please search again.'){
jQuery('.sidebar_loadMore').hide();
pageCount =1;
}
jQuery('.overBck').remove();
short_deals_attr();
}
});
pageCount++;
}
I have removed the unnesessory code. You can also look at the live site Bestofthebrunch
The best possible way that I found to serialize the function call in this case is a recursive call on the success of the previous one.
more_alt_leftajaxsearchcategory(0, 0, element=false, back = true, scroll["altscroll"] -1);
and updated the called function on success of Ajax by
if(back && loadtimes > 0){
more_alt_leftajaxsearchcategory(0, 0, element=false, back = true, loadtimes-1 );
}
like this...
var pageCount = 1;
function more_leftajaxsearchcategory(locval,pageid, element, back, times){
var offsetCount = pageCount * 10;
var dataString = jQuery("#leftformid").serialize();
jQuery.ajax({
url:"<?php echo admin_url('admin-ajax.php'); ?>",
type:'POST',
data: 'action=leftmenusearchcategory'+'&locationval='+locval+'&numb='+offsetCount+'&'+dataString,
beforeSend: function(){jQuery('body').append('<p class="overBck"><img alt="Loading..." src="<?php bloginfo('template_url'); ?>/images/ajax-loader.gif" style="margin-top:25%;"></p>'); },
success:function(results, status)
{
jQuery(".book_results").append(results);
if(results == 'Sorry no results found. Please search again.'){
jQuery('.sidebar_loadMore').hide();
pageCount =1;
}
if(back && loadtimes > 0){
more_alt_leftajaxsearchcategory(0, 0, element=false, back = true, loadtimes-1 );
}
jQuery('.overBck').remove();
short_deals_attr();
}
});
pageCount++;
}
In this method the function more_leftajaxsearchcategory is not called immediately without caring of the response of previous call. The function is called only when the first one get the response.
You have to add addtional param to pass in a callback that would be called when the ajax complete its work, then you can achieve something like this.
I'd use setTimeout with random length to fake as an ajax call:
var inserial = function(times) {
var count = 0;
function delay() {
var cur = count;
var length = Math.floor((Math.random() * 7) + 1) * 500;
$('<div>').text('#' + cur + ' will complete in ' + length).appendTo($('body'));
setTimeout(function() {
// Somthing in success ajax callback.
$('<div>').text('#' + cur + 'Done.').appendTo($('body'));
// Then call it to start next ajax load.
done();
}, length);
count++;
}
function done() {
if (count === times) {
$('<div>').text('All done').appendTo($('body'));
return;
}
delay();
}
// Your function, which aceepts a callback function.
delay();
};
inserial(5);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
Or if you love to embrace new techs, try Promise, it's a ES2015 feature while there are already libs implements it, it makes you easy to start all loading process at the same time, while keeps the order of the elements.
But you may need to learn its idea first, JavaScript Promises should be a good start.
var delay = function(id, cb) {
var length = Math.floor((Math.random() * 7) + 1) * 500;
$('<div>').text('#' + id + ' will complete in ' + length).appendTo($('body'));
// Wrap you ajax into promise.
return new Promise(function(resolve, reject){
setTimeout(function() {
// This will be called in ajax's success callback.
resolve(id);
}, length);
});
};
var i, arr = [];
// These function will all start to execute.
for (i = 0; i < 5; ++i) {
arr[i] = delay(i);
}
// But their results will be guranteed to put in order.
var sequence = Promise.resolve();
arr.reduce(function(seq, item) {
//
return seq.then(function() {
// the promise return from delay will get the resolved id as parameter.
return item;
}).then(function(id) {
// This is the commands in origin ajax's succes callback.
$('<div>').text('#' + id + 'Complete.').appendTo($('body'));
});
}, sequence);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.0/es6-shim.js"></script>
I need to pull data from a series of .csv files off the server. I am converting the csvs into arrays and I am trying to keep them all in an object. The ajax requests are all successful, but for some reason only the data from the last request ends up in the object. Here is my code:
var populate_chart_data = function(){
"use strict";
var genders = ["Boys","Girls"];
var charts = {
WHO: ["HCFA", "IWFA", "LFA", "WFA", "WFL"],
CDC: ["BMIAGE", "HCA", "IWFA", "LFA", "SFA", "WFA", "WFL", "WFS"]
};
var fileName, fileString;
var chart_data = {};
for (var i=0; i < genders.length; i++){
for (var item in charts){
if (charts.hasOwnProperty(item)){
for (var j=0; j<charts[item].length; j++) {
fileName = genders[i] + '_' + item + '_' + charts[item][j];
fileString = pathString + fileName + '.csv';
$.ajax(fileString, {
success: function(data) {
chart_data[fileName] = csvToArray(data);
},
error: function() {
console.log("Failed to retrieve csv");
},
timeout: 300000
});
}
}
}
}
return chart_data;
};
var chart_data = populate_chart_data();
The console in Firebug shows every ajax request successful, but when I step through the loops, my chart_data object is empty until the final loop. This is my first foray into ajax. Is it a timing issue?
There are two things you need to consider here:
The AJAX calls are asynchronous, this means you callback will only be called as soon as you receive the data. Meanwhile your loop keeps going and queueing new requests.
Since you're loop is going on, the value of filename will change before your callback is executed.
So you need to do two things:
Push the requests into an array and only return when the array completes
Create a closure so your filename doesn't change
.
var chart_data = [];
var requests = [];
for (var j=0; j<charts[item].length; j++) {
fileName = genders[i] + '_' + item + '_' + charts[item][j];
fileString = pathString + fileName + '.csv';
var onSuccess = (function(filenameinclosure){ // closure for your filename
return function(data){
chart_data[filenameinclosure] = csvToArray(data);
};
})(fileName);
requests.push( // saving requests
$.ajax(fileString, {
success: onSuccess,
error: function() {
console.log("Failed to retrieve csv");
},
timeout: 300000
})
);
}
$.when.apply(undefined, requests).done(function () {
// chart_data is filled up
});
I'm surprised that any data ends up in the object. The thing about ajax is that you can't depend on ever knowing when the request will complete (or if it even will complete). Therefore any work that depends on the retrieved data must be done in the ajax callbacks. You could so something like this:
var requests = [];
var chart_data = {};
/* snip */
requests.push($.ajax(fileString, {
/* snip */
$.when.apply(undefined, requests).done(function () {
//chart_data should be full
});