Google Chrome Average request time - ajax
On my project, I would like to optimize an ajax request and to know, on average, how many ms I have gained.
So, thanks to the Google Chrome network tab, I have the time of a request, something like that :
Is there a feature to have some stats about our request ? For example the average time.
If no, how to do that ?
Thanks !
Not too difficult to roll your own code in JavaScript.
var times = [];
var sum = 0;
var tries = 10
for(var i=0; i<tries; i++) {
var xhr = new XMLHttpRequest();
xhr.open("GET", window.location.href, false);
xhr.onload = (function() {
var time = (Date.now() - this.start);
times.push(time);
sum += time;
console.log("#" + this.number + " " + time + "ms");
}).bind(xhr);
xhr.number = (i + 1);
xhr.start = Date.now();
xhr.send(null);
}
console.log("avg: " + (sum / tries) + "ms");
Go to url: chrome://net-internals
Description here
Related
Is there a way to speed up/batch Google Calendar read/writes?
I am new to Google Apps Script and learning javascript as I go about this project. Over the course of the introductory codelabs I noted the best practice to read all the data into an array with one command, perform operations, and then write it with one command. I understood how to do this working with Google Sheets but how do I achieve this working with Google Calendar? I have come across a few links discussing batching with Google Calendar API and Advanced Google Services but I didn't understand how to make use of the information. I basically hope to batch edit events instead of accessing Google Calendar repeatedly in a for loop. function deleteMonth() { // Set Date range to delete var today = new Date(); var firstDay = new Date(today.getFullYear(), today.getMonth(), 1); var lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0); // read spreadsheet data and get User Info from ss var spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); var idSheet = spreadsheet.getSheetByName('User Info'); //Get users from sheet in array of objects with properties from column header in //'User Info' (name, email, year, calName, calID, early, late) var userInfo = getSheetData(idSheet); var deletedNames = ""; for (i = 0; i < userInfo.length; i++) { var calID = userInfo[i].calID; // if we have calID proceed to delete events if (calID) { console.time("get events"); var calendar = CalendarApp.getCalendarById(calID); var events = calendar.getEvents(firstDay, lastDay); console.timeEnd("get events"); // Delete events and add deleted name to string // deletedNames for (i = 0; i < events.length; i++) { console.time("delete event"); deletedNames += events[i].getTitle() + ", "; events[i].deleteEvent(); console.timeEnd("delete event"); } } } spreadsheet.toast("Deleted events: \n" + deletedNames); } Time output from console.time(): Other related links which sounded relevant: Using advanced google services (apps script resource) Google Developer blog?
I believe your goal as follows You want to delete all events in a month for several calendars using the batch process with Google Apps Script. You want to reduce the process cost of above process. For this, how about this answer? Issue and workaround: Calendar API can process with the batch requests. The batch requests can run 100 requests by one API call and can process with the asynchronous process. By this, I think that the process cost can bereduced. But, in the current stage, unfortunately, several calendar IDs cannot be used in one batch. When the requests including several calendar IDs, an error of Cannot perform operations on different calendars in the same batch request. occurs. So in your case, all events in a month in one calendar ID can be deleted in one batch requests. It is required to request the number of calendar IDs. I would like to propose this as the current workaround. By the way, as the modification point of your scrit, in your script, the variable i is used in 1st for loop and 2nd for loop. By this, all values of userInfo are not used. Please be careful this. Sample script: Before you run the script, please enable Calendar API at Advanced Google services. function deleteMonth() { var today = new Date(); var firstDay = new Date(today.getFullYear(), today.getMonth(), 1); var lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0); var spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); var idSheet = spreadsheet.getSheetByName('User Info'); var userInfo = getSheetData(idSheet); var deletedNames = ""; var requests = []; // For batch requests. for (i = 0; i < userInfo.length; i++) { var req = []; var calID = userInfo[i].calID; if (calID) { var calendar = CalendarApp.getCalendarById(calID); var events = calendar.getEvents(firstDay, lastDay); for (j = 0; j < events.length; j++) { deletedNames += events[j].getTitle() + ", "; var e = events[j]; req.push({ method: "DELETE", endpoint: `https://www.googleapis.com/calendar/v3/calendars/${calID}/events/${e.getId().replace("#google.com", "")}`, }); } } requests.push(req); } // Run batch requests. requests.forEach(req => { const limit = 100; const split = Math.ceil(req.length / limit); const boundary = "xxxxxxxxxx"; for (let i = 0; i < split; i++) { const object = {batchPath: "batch/calendar/v3", requests: req.splice(0, limit)}; const payload = object.requests.reduce((s, e, i) => s += "Content-Type: application/http\r\nContent-ID: " + i + "\r\n\r\n" + e.method + " " + e.endpoint + "\r\nContent-Type: application/json; charset=utf-8\r\n\r\n" + JSON.stringify(e.requestBody) + "\r\n--" + boundary + "\r\n", "--" + boundary + "\r\n"); const params = {method: "post", contentType: "multipart/mixed; boundary=" + boundary, payload: payload, headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}, muteHttpExceptions: true}; var res = UrlFetchApp.fetch("https://www.googleapis.com/" + object.batchPath, params); console.log(res.getContentText()) } }) spreadsheet.toast("Deleted events: \n" + deletedNames); } Note: Please use this script with V8. References: Advanced Google services Sending Batch Requests Events: delete
How to speed up creating Waypoints?
I am attaching a waypoint to 2000 elements on the page, because I want to trigger a different event for each one. I have tried the noframework way as well as using jquery, and each one adds about 20 seconds to the loading of my page. Is there a technique I can use to attach waypoints to a lot of elements without it slowing down my page load? Thanks. Jquery version (call once): var waypoints = $('[data-waypointidentifier="iswaypoint"]').waypoint({ handler: function (direction) { waypointHandler(direction, this); } }); No Framework version (called once for each of the 2000 elements): var waypoint = new Waypoint({ document.getElementById('element-waypoint1'), handler: function (direction) { waypointHandler(direction, this); } });
This is not really an answer BUT does demonstrate the issue in near lowest form. EDIT: This is perhaps a bit of a workaround hack now. Modified to simply attach in chunks. NOTE: you will have to modify the last chunk - mine had even numbers at 2000 which I "chunked" in groups and then set a bound timeout to attach them. It still takes just as long but makes the situation slightly less visible while/until they all attach. // approx times to attach (not definitive) // - note each increment nearly doubles times, not linear // super simple test, chrome, prob more on most computers // var punchCount = 250; // 150ms // var punchCount = 500; // 500ms // var punchCount = 1000; // 2350ms // var punchCount = 1500; // 4700ms var punchCount = 2000; // 8750ms function initme() { var addme = $('<div class="facepunch" data-iam="-1">hi</div>'); var addgroup = $('<div>start</div>'); for (var i = punchCount; i > 0; i--) { addme = $('<div class="facepunch">hi</div>').data('iam', i); addme.appendTo(addgroup); } $('#punchme').append(addgroup); } var t0 = performance.now(); console.log('init 1'); initme(); var t1 = performance.now(); console.log("Call to initme took " + (t1 - t0) + " milliseconds."); console.log('before 1'); function waypointHandler(direction, me) { // NOOO... this will kill your perf console.log(direction, me); var newt = "howdy " + $(me.element).data('iam'); $(me.element).text(newt + " " + direction); } t0 = performance.now(); console.log('before 2'); var waypoints = {}; function createWaysChunk(begin, end) { console.log(begin, end, $('.facepunch').slice(begin, end).length); $('.facepunch').slice(begin, end).waypoint({ // enabled: false,//no perf benefit handler: function(direction) { waypointHandler(direction, this); // "this.element" is the element } }); } function createWays() { t0 = performance.now(); var chunksize = 40; var y = punchCount / chunksize; // 250 var x = 0; for (var i = 0; i < chunksize; ++i) { begin = x; end = y * i; setTimeout(createWaysChunk.bind(null, begin, end), 100); x = end; } t1 = performance.now(); console.log("Call to createways = took " + (t1 - t0) + " milliseconds."); } // time out just moves the "freeze" to after the page loads //var timeoutID0 = window.setTimeout(createWays, 2000); window.onload = createWays; // createWays(); t1 = performance.now(); console.log("Call to waypoints = took " + (t1 - t0) + " milliseconds."); // Test with .enableAll //t0 = performance.now(); //console.log('after 1'); // time out just moves the "freeze" to after the page loads //var timeoutID = window.setTimeout(Waypoint.enableAll, 2000); //initial enabled = false then enable is way slow, slower than true overall by 15000+ ms //Waypoint.enableAll(); //console.log('after 2'); //t1 = performance.now(); //console.log("Call to enableAll took " + (t1 - t0) + " milliseconds."); .facepunch { height: 3em; border: solid 1px lime; margin: 0.25em; } <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/waypoints/4.0.1/jquery.waypoints.min.js" integrity="sha256-jDnOKIOq2KNsQZTcBTEnsp76FnfMEttF6AV2DF2fFNE=" crossorigin="anonymous"></script> <div id="punchme"></div>
is my if statement doing what i think its doing?
Here I have tis function that is querying data and returning it to me and im putting that data in to html elements to make a post.my if statement at the bottom is where im having a bit of problem i trying to only apply my comment window once to the new clones once they have been pushed over to the new div called story board, i believe im telling my if statement that if the class already exists in that new clone then do nothing else apply it there.. to seee what i am talking about...here is my test domain...http://subdomain.jason-c.com/ sign in is "kio" pass is the same and when you hit publish on the stories, everytime a nw one hits it will apply comment box to a post in the storyboard window that already has a comment text area. what am i doing wrong. function publishWindowHandler(){ var query = new Parse.Query('Post'); console.log(currentUser); query.equalTo("User", currentUser); query.include("User"); query.descending("createdAt") console.log(user.get('username')); query.find({ success:function(results){ document.getElementById("publishCenter").textContent = ""; for(var i =0; i < results.length; i++){ var userPost = results[i]; //console.log(userPost.get("User") + " / " + userPost.get("Author") + " / " + userPost.get("Story") + " / " + userPost.get("objectId")); var authorTitle = document.createElement("p"); var newPost = document.createElement("P"); var title = document.createElement("P"); var userLabel = document.createElement("p"); var postId = userPost.id; var postBtn = document.createElement("INPUT"); postBtn.className ="publishBtn"; postBtn.id ="publishBtn"; postBtn.setAttribute("Type", "button"); postBtn.setAttribute("value", "Publish"); title.textContent = "Story: " + userPost.get("Title"); authorTitle.textContent = "Author: " + userPost.get("Author"); newPost.textContent = userPost.get("Story"); userLabel.textContent = "Published by: " +userPost.get("User").get ("username"); var postWrapper = document.createElement("DIV"); postWrapper.className = "postWrapper"; postWrapper.id = postId; document.getElementById("publishCenter").appendChild(postWrapper); postWrapper.appendChild(title); postWrapper.appendChild(authorTitle); postWrapper.appendChild(newPost); postWrapper.appendChild(userLabel); postWrapper.appendChild(postBtn); postBtn.addEventListener("click", publicViewHandler); function publicViewHandler(){ $(this).parent(".postWrapper").clone().appendTo(".storyBoard"); function testWindow(){ if($(publicBoard).children().hasClass(".commentWindow")){ } else { $(".storyBoard").children().append(commentWindow); } } testWindow(); } } } }) }
According to the documentation, jquery hasClass doesn't need '.' prefixing the passed in class name. https://api.jquery.com/hasclass/ Try removing that and see if that get's you anywhere. Also, where is the variable commentWindow defined? Is it global?
var myClone = $(this).parent().clone(true); myClone.appendTo(".storyBoard"); console.log(publicBoard); console.log("hello",$(this)); console.log($(publicBoard).find('.postWrapper').find("commentWindow")); myClone.append($(commentWindow).clone()); this is what i ended up doing to solve my issue took me a while and a little help from a friend.
Calculating Processing and Request time for multiple Asynchronous Requests
For data visualization request in my application, I am sending multiple AJAX requests to a servlet in order to get the data in chunks and on callback of each request, the data received is rendered over map. For this request, I am trying to calculate: Request Time (how much total time it took for client to get data from server) Processing Time (how much total time it took for client to render the data on client side) In order to do this, I am capturing start time of each request before sending it to server (using jquery "beforeSend") and "onSuccess" event of each request, the end time is captured. Once the all requests are completed, I am deducting the "start time" of first request from the "end time" of last request in order to calculate the total time the client took for fetching records from server. (Similarly for Processing Time) But somehow my calculation doesn't produce correct results. Could any one please provide me some suggestions for this issue? for explaining my question in more better way: var dataProviderRequestsStartTime = []; var dataProviderRequestsEndTime = []; var dataParsingStartTime = []; var dataParsingEndTime = []; getResults(ids); var getResults = function(totalIds) { for(var i=0; i<10; i++;) { requestResultForOneChunk(totalIds[i]); } }; var requestResultForOneChunk = function(streetIds) { $.ajax({ beforeSend: function() { var requestStartTime = new Date().getTime(); dataProviderRequestsStartTime.push(requestStartTime); }, type : 'POST', url : "myServlet", contentType : "application/x-www-form-urlencoded", data : { "ids" : streetIds, }, success : function(response) { //Request Finished var dataProvideRequestEndTime = new Date().getTime(); dataProviderRequestsEndTime.push(dataProvideRequestEndTime); addFeaturesToMap(response); }, error : function(x, e) { alert("Something went wrong in the request" + e); } }); }; var addFeaturesToMap = function(measurements) { //Parsing Started var featureParsingStartTime = new Date().getTime(); dataParsingStartTime.push(featureParsingStartTime); doParsing(measurements); //Parsing Finished featureParsingEndTime = new Date().getTime(); dataParsingEndTime.push(featureParsingEndTime); }; $("#loading").bind( "ajaxStart", function(options) { ajaxStartTime = options.timeStamp; }).bind("ajaxStop", function(options) { var ajaxEndTime = options.timeStamp; var totalTime = (ajaxEndTime - ajaxStartTime); calculateTimeBreakDown(); }); var calculateTimeBreakDown = function() { var totalValues = dataProviderRequestsEndTime.length; var lastValueIndex = totalValues - 1; // Request Time calculation var endTimeOfLastRequest = dataProviderRequestsEndTime[lastValueIndex]; var startTimeOfFirstRequest = dataProviderRequestsStartTime[0]; var totalRequestTime = (endTimeOfLastRequest - startTimeOfFirstRequest); // Parsing Time Calculation var endTimeOfLastParsing = dataParsingEndTime[lastValueIndex]; var startTimeOfFirstParsing = dataParsingStartTime[0]; var totalParsingTime = (endTimeOfLastParsing - startTimeOfFirstParsing); }; Finally, I have requestTime(totalRequestTime) and parsingTime(totalParsingTime). But the problem is adding these both doesn't produce value near to total time which is calculated using ajax start and stop.
look at the .ajaxStart() and .ajaxStop() events for "total time", (<- those are also great for progressbars) http://api.jquery.com/ajaxStart/ http://api.jquery.com/ajaxStop/ and .ajaxSend() and .ajaxComplete() events for "cumulative time" calculations. http://api.jquery.com/ajaxSend/ http://api.jquery.com/ajaxComplete/ look at this code: var totalTime = null; var cachedTime = null; function alertLoadingTime() { if(!totalTime) return; var loadingTime = totalTime / 1000; console.log("loaded " + loadingTime + " seconds"); } function timingStart() { cachedTime = new Date; } function timingEnd() { var endTime = new Date; totalTime += endTime - cachedTime; cachedTime = null; alertLoadingTime(); } $(document).ajaxStart(timingStart); $(document).ajaxStop(timingEnd); note that it will only account for time spent doing ajax calls and won't include the initial page loading time. to time the parsing: use the same functions as before but change totalTime to totalParsingTime. (Note: you can achieve this by changing totalTime to reference some other variable) Call timingStart() right before you append the result of the ajax call to the dom tree. Have the server add timingEnd() to the end of every response. totalTime will then be set to the time it took to add everything to the DOM tree.
solution for you would to rely on jquery ajax callback methods ajaxStart : Register a handler to be called when the first Ajax request begins. ajaxStop : Register a handler to be called when all Ajax requests have completed including success and error callbacks I have used the below code snippet in my application and it works perfectly fine to report page rendering time including ajaxs. var startTime = 0,endTime = 0; $(document).ajaxStart(function(){ startTime = new Date(); }); $(document).ajaxStop(function(){ endTime = new Date(); console.log("total time required : ",endTime - startTime); //Total time in milliseconds including time spent in success or error callbacks });
Inconsistent data corruption in a store loaded through a proxy reader
We're experiencing inconsistent data corruption in a store loaded through a proxy reader. It's browser dependant. Works fine in Chrome and Safari on the desktop all the time. On our two testing iPhones it intermittently breaks depending on the data the store has loaded, larger data volume seems to cause more breaks. We can't see any errors or patterns in the JSON data where it breaks. Symptoms: data loads, but some of the data belonging to the last few items is missing a read listener attached to the store doesn't fire when this data loss occurs, it does fire on the desktop browser where we experience not data loss The piece of data that always seems to be lost is the start_time. We've been trying to debug this one for a while now and are really stumped. Thank you for any ideas you might have as to why this is occurring. Our regModel Ext.regModel('Booking', { fields: ['id', 'start', 'end', 'title', 'service', 'service_id', 'client_note', 'stylist_note', 'utc_start','day_date', 'start_time', 'end_time', "home_ph", "mobile_ph", 'work_ph', 'work_ext', 'email', 'utc_end'] }); Our store var store = new Ext.data.JsonStore({ model : 'Booking', sorters: 'utc_start', autoLoad: false, proxy: { type: 'ajax', url: '../includes/get_appointments.php', extraParams: { req_start: '1295251200', req_end: '1295856000' }, reader: { type: 'json', root: 'events' } }, listeners: { load:function(store, records, success) { bookings.setLoading(false); // check to see length of records, start i from there for (var i = 0; i < records.length; i++){ utc_start = records[i].get('utc_start'); utc_end = records[i].get('utc_end'); // create day of week // y,m,d // time var this_start_date = new Date((utc_start * 1000)); var this_end_date = new Date((utc_end * 1000)); var day_of_week = days_of_week[this_start_date.getDay()]; var date_of_month = this_start_date.getDate(); var month_of_year = month_names[this_start_date.getMonth()]; var full_year = this_start_date.getFullYear(); var start_military_hours = this_start_date.getHours(); var this_start_minutes = this_start_date.getMinutes(); var end_military_hours = this_end_date.getHours(); var this_end_minutes = this_end_date.getMinutes(); if(this_start_minutes == "0") { this_start_minutes = "00"; } if(parseInt(start_military_hours) < 12) { var start_time = start_military_hours + ":" + this_start_minutes + " AM"; } else if(parseInt(start_military_hours) == 12) { var start_time = start_military_hours + ":" + this_start_minutes + " PM"; } else { var start_time = (parseInt(start_military_hours) - 12) + ":" + this_start_minutes + " PM"; } if(this_end_minutes == "0") { this_end_minutes = "00"; } if(parseInt(end_military_hours) < 12) { var end_time = end_military_hours + ":" + this_end_minutes + " AM"; } else if(parseInt(end_military_hours) == 12) { var end_time = end_military_hours + ":" + this_end_minutes + " PM"; } else { var end_time = (parseInt(end_military_hours) - 12) + ":" + this_end_minutes + " PM"; } var day_date = day_of_week + ", " + full_year + " " + month_of_year + " " + date_of_month; if(records[i].get('service_id') == 0) { records[i].set('title', 'Booked off'); records[i].set('service', ''); } records[i].set('day_date', day_date); records[i].set('start_time', start_time); records[i].set('end_time', end_time); } if(store.proxy.reader.rawData.next_page_num == undefined) { store.start = store.proxy.reader.rawData.prev_page_num; } else { store.currentPage = store.proxy.reader.rawData.next_page_num; } }, read:function(store,records,success){ Ext.Msg.alert('data read'); } }, getGroupString : function(record) { return record.get('day_date'); // optional char from array removed } }); Our JSON {"events":[{"id":"3739","start":"2011-01-18T10:00:00-08:00","end":"2011-01-18T11:45:00-08:00","title":"Jen Cannor","service":"Haircut & Highlights","service_id":"67","client_note":"","stylist_note":"Looking forward to seeing Jen when she comes in next.","utc_start":"1295373600","email":"jen.c#cannorfarms.net","home_ph":"232-433-2222","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1295379900"},{"id":"3740","start":"2011-01-18T12:00:00-08:00","end":"2011-01-18T13:30:00-08:00","title":"Michelle Steves","service":"Root Colour","service_id":"69","client_note":"","stylist_note":"","utc_start":"1295380800","email":"michelle5b64#telus.net","home_ph":"604-555-5555","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1295386200"},{"id":"3741","start":"2011-01-18T13:30:00-08:00","end":"2011-01-18T14:00:00-08:00","title":"Amanda Brenner","service":"Wash & blow dry","service_id":"70","client_note":"","stylist_note":"","utc_start":"1295386200","email":"amandab#coastfitness.com","home_ph":"555-235-2366","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1295388000"},{"id":"3742","start":"2011-01-18T14:00:00-08:00","end":"2011-01-18T15:45:00-08:00","title":"Janice Potters","service":"Haircut & Colour","service_id":"66","client_note":"","stylist_note":"","utc_start":"1295388000","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1295394300"},{"id":"3743","start":"2011-01-18T15:45:00-08:00","end":"2011-01-18T16:45:00-08:00","title":"Angus Middleton","service":"Men's haircut","service_id":"61","client_note":"","stylist_note":"","utc_start":"1295394300","email":"angusman#hotmaile.com","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1295397900"},{"id":"3025","start":"2011-01-19T08:00:00-08:00","end":"2011-01-19T09:45:00-08:00","title":"Jen Cannor","service":"Haircut & Highlights","service_id":"67","client_note":"","stylist_note":"","utc_start":"1295452800","email":"jen.c#cannorfarms.net","home_ph":"232-433-2222","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1295459100"},{"id":"3026","start":"2011-01-19T10:00:00-08:00","end":"2011-01-19T11:45:00-08:00","title":"Karen Walker","service":"Haircut & Colour","service_id":"66","client_note":"","stylist_note":"","utc_start":"1295460000","email":"karenwalker#officesurplusdirect.net","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1295466300"},{"id":"3027","start":"2011-01-19T11:45:00-08:00","end":"2011-01-19T12:45:00-08:00","title":"Amanda Brenner","service":"Women's Haircut","service_id":"65","client_note":"","stylist_note":"","utc_start":"1295466300","email":"amandab#coastfitness.com","home_ph":"555-235-2366","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1295469900"},{"id":"3028","start":"2011-01-19T13:00:00-08:00","end":"2011-01-19T14:30:00-08:00","title":"Mary Thacker","service":"Root Colour","service_id":"69","client_note":"","stylist_note":"","utc_start":"1295470800","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1295476200"},{"id":"3029","start":"2011-01-19T14:30:00-08:00","end":"2011-01-19T15:00:00-08:00","title":"Malcolm Anderson","service":"Men's haircut","service_id":"61","client_note":"","stylist_note":"","utc_start":"1295476200","email":"malcolm#testserveraddy.com","home_ph":"240-444-4444","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1295478000"},{"id":"4856","start":"2011-03-09T09:00:00-08:00","end":"2011-03-09T10:00:00-08:00","title":"Simon Chalk","service":"Men's haircut","service_id":"61","client_note":"","stylist_note":"","utc_start":"1299690000","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299693600"},{"id":"4858","start":"2011-03-09T10:00:00-08:00","end":"2011-03-09T10:15:00-08:00","title":"Brian Lytton","service":"Men's haircut","service_id":"61","client_note":"","stylist_note":"","utc_start":"1299693600","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299694500"},{"id":"4859","start":"2011-03-09T10:15:00-08:00","end":"2011-03-09T10:30:00-08:00","title":"Brad Wicker","service":"Men's haircut","service_id":"61","client_note":"","stylist_note":"","utc_start":"1299694500","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299695400"},{"id":"4860","start":"2011-03-09T10:30:00-08:00","end":"2011-03-09T10:45:00-08:00","title":"Brad Wicker","service":"Men's haircut","service_id":"61","client_note":"","stylist_note":"","utc_start":"1299695400","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299696300"},{"id":"4861","start":"2011-03-09T10:45:00-08:00","end":"2011-03-09T11:00:00-08:00","title":"Brian Lytton","service":"Men's haircut","service_id":"61","client_note":"","stylist_note":"","utc_start":"1299696300","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299697200"},{"id":"4862","start":"2011-03-09T11:00:00-08:00","end":"2011-03-09T11:15:00-08:00","title":"Brian Lytton","service":"Men's haircut","service_id":"61","client_note":"","stylist_note":"","utc_start":"1299697200","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299698100"},{"id":"4863","start":"2011-03-09T11:15:00-08:00","end":"2011-03-09T11:30:00-08:00","title":"Simon Chalk","service":"Men's haircut","service_id":"61","client_note":"","stylist_note":"","utc_start":"1299698100","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299699000"},{"id":"4864","start":"2011-03-09T11:30:00-08:00","end":"2011-03-09T11:45:00-08:00","title":"Chester Welling","service":"Men's haircut","service_id":"61","client_note":"","stylist_note":"","utc_start":"1299699000","email":"chester#eastern.pharma.net","home_ph":"604-555-5555","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299699900"},{"id":"4865","start":"2011-03-09T11:45:00-08:00","end":"2011-03-09T12:00:00-08:00","title":"Brad Wicker","service":"Men's haircut","service_id":"61","client_note":"","stylist_note":"","utc_start":"1299699900","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299700800"},{"id":"4866","start":"2011-03-09T12:00:00-08:00","end":"2011-03-09T13:00:00-08:00","title":"Janice Potters","service":"Women's Haircut","service_id":"65","client_note":"","stylist_note":"","utc_start":"1299700800","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299704400"},{"id":"4867","start":"2011-03-09T13:00:00-08:00","end":"2011-03-09T13:15:00-08:00","title":"Jacqui Chan","service":"Women's Haircut","service_id":"65","client_note":"","stylist_note":"","utc_start":"1299704400","email":"jc#rebelfrontier.net","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299705300"},{"id":"4876","start":"2011-03-09T13:15:00-08:00","end":"2011-03-09T13:30:00-08:00","title":"Mary Thacker","service":"Women's Haircut","service_id":"65","client_note":"","stylist_note":"","utc_start":"1299705300","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299706200"},{"id":"4868","start":"2011-03-10T10:15:00-08:00","end":"2011-03-10T11:15:00-08:00","title":"Trisha Roberts","service":"Women's Haircut","service_id":"65","client_note":"","stylist_note":"","utc_start":"1299780900","email":"trb483408#gmail.com","home_ph":"604-555-5555","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299784500"},{"id":"4870","start":"2011-03-10T11:30:00-08:00","end":"2011-03-10T12:30:00-08:00","title":"Jenson Bryant","service":"Women's Haircut","service_id":"65","client_note":"","stylist_note":"","utc_start":"1299785400","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299789000"},{"id":"4872","start":"2011-03-10T12:45:00-08:00","end":"2011-03-10T13:00:00-08:00","title":"Jenson Bryant","service":"Women's Haircut","service_id":"65","client_note":"","stylist_note":"","utc_start":"1299789900","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299790800"},{"id":"4873","start":"2011-03-10T13:00:00-08:00","end":"2011-03-10T13:15:00-08:00","title":"Jenson Bryant","service":"Women's Haircut","service_id":"65","client_note":"","stylist_note":"","utc_start":"1299790800","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299791700"},{"id":"4874","start":"2011-03-10T13:15:00-08:00","end":"2011-03-10T14:15:00-08:00","title":"Simon Chalk","service":"Men's haircut","service_id":"61","client_note":"","stylist_note":"","utc_start":"1299791700","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299795300"},{"id":"4875","start":"2011-03-11T10:15:00-08:00","end":"2011-03-11T11:15:00-08:00","title":"Karen Walker","service":"Women's Haircut","service_id":"65","client_note":"","stylist_note":"","utc_start":"1299867300","email":"karenwalker#officesurplusdirect.net","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299870900"},{"id":"4877","start":"2011-03-11T12:00:00-08:00","end":"2011-03-11T12:15:00-08:00","title":"Amanda Brenner","service":"Women's Haircut","service_id":"65","client_note":"","stylist_note":"","utc_start":"1299873600","email":"amandab#coastfitness.com","home_ph":"555-235-2366","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299874500"},{"id":"4878","start":"2011-03-11T12:30:00-08:00","end":"2011-03-11T13:30:00-08:00","title":"Arnold Fieldman","service":"Men's haircut","service_id":"61","client_note":"","stylist_note":"","utc_start":"1299875400","email":"","home_ph":"","mobile_ph":"","work_ph":"","work_ext":"","utc_end":"1299879000"}], "next_page_num":"7"}
This is not a Sencha Touch specific issue: there are limits on the size of Ajax response that mobile safari can handle: see Too large an AJAX response for mobile safari? Update: apparently this is not a mobile safari problem, this is a cellular network problem. Some networks will helpfully "paginate" the Ajax call -- thinking it's a regular web page download. Can you check to see if this is still the case on a wifi network?
After much head banging it appears that Sencha Touch stores only work well with up to approximately 10,000 characters I'm guess 8192 + the json delimiters? After that it seems to start losing part of the data for us. Can anyone else corroborate this?