How to delay the inline javascript loading time? - javascript

In my usecase, I am using both external and inline javascript contents. I have the following structure.
app/
header.html
home.html
config-load.js
footer.html
home.html includes header.html and footer.html. header.html file includes config-load.js.
config-load.js makes an ajax call to get the configs based on the stage from golang backend. This may have few milliseconds delay.
There are few inline scripts in home.html which uses the configs collected by config-load.js ajax call.
So config-load.js ajax call must be completed before inline scripts are loaded. But it is loading in the other way around.
I tried to use a while loop to delay the load time for the inline scripts as below,
while(configReceived == false)
{
setTimeout(function(){
console.log("waiting for config");
}, 2000);
}
if(configReceived)
{
//process configs
}
But this blocks the thread. The page is stuck in the while loop. Is there any other way to achieve this?
EDIT 1 :
Here is the inline script content,
<script type="text/javascript">
window.onload = function() {
time = new Date($.now());
var tagsArray = ["C", "C++", "Go", "Ruby"];
//var tagsArray = [];
requestJSON = '{"Method":"GET","AppName":"Web-app","ServiceURL":"'+endpoints.Tags.HTTPEndpoint.URL+'","Properties":null,"Object":"","Timestamp":"'+time+'"}'
$.ajax({
type: "GET",
url: endpoints.Tags.HTTPEndpoint.URL,
data: requestJSON,
processData: false,
contentType: "application/json;",
dataType: "json",
async: false,
success: function(data){
console.log("tags retrieved successfully info updated successfully")
console.log("Tags ", data.Object)
tagsArray = data.Object
},
failure: function(errMsg) {
console.log("Error occured in getting tags ", errMsg)
}
});
$("#myTags").tagit();
$("#tags").tagit({
fieldName: "tagsName", // The name of the hidden input field
availableTags: tagsArray,
allowSpaces:true,
caseSensitive:false,
removeConfirmation:true,
placeholderText:"Tags",
tagLimit: 5,
allowDuplicates: false,
singleField: true, // Use a hidden input element with the fieldName name
singleFieldDelimiter: ',', // Optional, default value is same.
onlyAvailableTags: false
});
}
</script>
And my config-load.js looks like below,
//////////////////////////////////////////////////////////
// code block to get the service endpoints by stage starts
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
endpoints = JSON.parse(xhr.responseText);
console.log("server endpoints be ", endpoints);
configReceived = true;
}
}
xhr.open("GET", "/config", true);
try {
xhr.send();
} catch (err) {
// handle error
console.log("Error occured in getting the service endpoints. This may break all ajax services");
}
// code block to get the service endpoints by stage ends
////////////////////////////////////////////////////////
I am trying for last 3 days but no luck.

"while loop" is synchronous, which means it will block the thread and makes whole application stuck.
Javascript async scripts's execution order is not guaranteed, so you should use the "callback" or in ES6 you can use promise, ES7 you can use async, await.
Anyway the better way is wrap your config-load.js javascript code in a function, if you use Jquery's ajax api, the code may looks like this:
function loadConfigAjax(callback){
$.ajax({url: "http://myconfig", success: function(config){
callback(config)
}});
}
And in your inline javascript may looks like this
<script type="text/javascript">
window.onload = function() {
var configReceived = function(config){
//process configs
};
// pass the configReceived as callback
// so that configReceived function will always be invoked after config received
loadConfigAjax(configReceived);
}
</script>

Be aware that javascript is asynchronous and you don't have full control over the loading sequences of the scripts, unless you are not using the async await new javascript feature or promises. But in your case is not really needed for these.
First thing you need to do is to include the config-load.js in the head section, right on the top, this way you have some guarantee that the file is loaded before the DOM is getting populated.
Another thing is to use the window.onload function inside the inline scripts, to force the browser to parse the scripts only after all the DOM structure has been constructed and fully populated.
So inside your html section wrap your function into the window.onload function callback:
<script type="text/javascript">
window.onload = function() {
while(configReceived == false)
{
setTimeout(function(){
console.log("waiting for config");
}, 2000);
}
if(configReceived)
{
//process configs
}
}
</script>
EDIT:
There are quite a few errors in your approach. First and foremost there is no need to call the ajax requests in two separate scripts. Using the above mentioned promise technique you can chain the responses. Here is a short example of how the jquery promises are working:
function first() {
return $.ajax(...);
}
function second(data, textStatus, jqXHR) {
return $.ajax(...);
}
function third(data, textStatus, jqXHR) {
return $.ajax(...);
}
function main() {
first().then(second).then(third);
}
Remember: a call in the chain group return the response. Which means that you can delegate the response to the next chain, which means that when the request has been resolved you can pass through the result to the next call.
Applying to your example when you receive the response by calling the endpoints service, you can pass the result as parameter to the next call, and this will be accessed only when the response from the first call will be resolved.
Check this fiddle as an example.
Applying this technique it's not needed needed anymore to check configReceived = true;.
Another thing you have to make sure is that jQuery is included before you are trying to call jQuery.ajax.
Here are some references about promises:
http://www.danieldemmel.me/blog/2013/03/22/an-introduction-to-jquery-deferred-slash-promise/
http://www.bitstorm.org/weblog/2012-1/Deferred_and_promise_in_jQuery.html
https://davidwalsh.name/write-javascript-promises

Related

How to execute a Javascript function after another has completed using promises?

I wish to refresh the page after values have been saved to a database, using js promises.
My code is wrapped inside a jQuery event listener:
$("img[class=okButton]").click(function(){
var field_userid = $(this).attr("id");
doThisFirst();
// then make a promise
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(500).then(() => writeNewRoom(field_userid)); // function to write to database
refreshPage(); // after write has finished
});
///////////////////
function writeNewRoom(field_userid)){
// ajax to do something;
}
///////////////////
function refreshPage(){
if(window.confirm("Click to refresh")){location = location}
}
The intended behaviour is to process data first, then finish "doing something" in the writeNewRoom() function before refreshing the page in the refreshPage() function.
What is actually happening is that the first doThisFirst() function is processed correctly, but then the window.confirm box in the third function, pops up BEFORE the writeNewRoom function has run.
I've never used promises before, so can anyone help figure out what went wrong? I took the basic syntax from the mozilla website: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
Any help is much appreciated.
In your case, you would want to put a call back in your writeNewRoom() method.
For example, you call whatever you need to do on the .click() function and put a .done() method when your ajax call for writing to the database is done.
$("img[class=okButton]").click(function(){
var field_userid = $(this).attr("id");
doThisFirst();
// Remove the Promise
writeNewRoom(field_userid); // function to write to database
});
function writeNewRoom(field_userId) {
$.ajax({
url: "/someurl",
method: "method",
data: {
a: field_userId
}
}).done(function(data) {
console.log('success', data)
refreshPage(); // This is where you refresh the page.
}).fail(function(xhr) {
console.log('error', xhr);
});
}
If your // ajax to do something; returns a promise (jQuery.ajax() does) you can do it like this:
wait(500).then(() => writeNewRoom(field_userid))
.then(() => refreshPage());
There's also one extra parenthesis here function writeNewRoom(field_userid))
if the writeNewRoom(field_userid) is doing an ajax call, you put the refreshPage()-function into the callback of the ajax call, so it is executed AFTER the ajax has finished, e.g:
function writeNewRoom(field_userid)){
$.ajax({
url: "someUrl",
type: 'GET',
success: (result) => {
refreshPage() //when ajax has succeded, refresh page
},
error: (err) => {
//do something else
}
});
}

How do I extend JQuery's ajax done() method?

I have a jQuery ajax function like this:
jQuery.ajax({
url : '/blabla',
method : 'post',
data: {
bla : bla
}
}).done(function(data) {
// do lots of stuff
});
.. and I want to be able to add a check that the data passed into the done callback function doesn't have a session_timed_out value in it. Say I have many functions similar to the one above but they all do different things, but they ALL need to check if the session timed out first. Is there a proper way to extend done() so it initially checks for a timeout? I tried to do something like this but it failed:
var myAjax = function(options,callback){
var defaults = {
done: function(data){ //hijack the success handler?
if(check(data)){
callback(data);
}
}
};
jQuery.extend(options,defaults);
return jQuery.ajax(options);
}
When I use this extended function it works like before, meaning the check never gets called because it seems to be superseded by the done() callback in the actual implementation, which I guess makes sense. So I want to know if there is a way to "decorate" or extend done() function so it initially checks for the session timeout first. Or will I need to manually add this same session check to all of my ajax done's?
This snippet overrides the jQuery ajax method so you can add an extra check when it successfully returns.
(function($) {
var yourCustomCheck = function(ajaxRes) {
// Do whatever you need and return a boolean
};
var oldAjax = $.ajax;
$.ajax = function(opts) {
return $.Deferred(function() {
var _def = this;
oldAjax.call(this, opts).done(function(res) {
console.log("this is done first");
if(yourCustomCheck.call(this, res)) _def.resolve(res);
else _def.reject("timeout");
}).fail(function() {
_def.reject();
});
})
}
})(jQuery);
After this, you can use $.ajax() normally..
$.ajax({
.....
}).done(function(res) {
console.log("ok");
}).fail(function() {
console.log("no ok");
});
Here is a jsfiddle with a working example: https://jsfiddle.net/jormaechea/kffyo7qL/1/
You could chain a timeout checker:
jQuery.ajax({
url : '/blabla',
method : 'post',
data: {
bla : bla
}
}).then(timeoutCheck).then(function(data) {
// do lots of stuff
}, function(err) {
// handle error
});
function timeoutCheck(data) {
if (check(data)) {
return data;
} else {
// return a rejected promise to turn fulfilled into reject
return jQuery.Deferred.reject(new Error("timeout"));
}
}
Or, you could put this in your own ajax wrapper.
jQuery.ajaxT = function() {
return jQuery.ajax.apply(jQuery, arguments).then(timeoutCheck);
}
jQuery.ajaxT(...).then(function(results) {
// handle returned data here
// the timeoutCheck has already been done
}, function(err) {
// handle any errors here
});
Then, any ajax call you initiated with jQuery.ajaxT() would automatically have the timeoutCheck added to it's promise logic. If the ajax call succeeds and the timeout check passes, then the promise is fulfilled. If the ajax call succeeds and the timeout check fails, then the promise rejected.

New $.Deferred object with the old callbacks

Please forgive me if this is a stupid question. I have been trying for hours and my brain have just stopped working.
I have such system that consists of three AJAX calls. Server response of first call usually is a 200 Success; but second and third queries are fragile because they are image uploading, and on the server side, I have so much validation rules that client's images mostly fail.
window.AjaxCall = function () {
// to pass to $.ajax call later
this.args = arguments;
// xhr status
this.status = null;
// xhr results (jqXHR object and response)
this.xhrResponse = {};
this.dfr = new $.Deferred();
// to provide an easier interface
this.done = this.dfr.done;
this.fail = this.dfr.fail;
this.then = this.dfr.then;
};
AjaxCall.prototype.resetDfr = function () {
this.dfr = new $.Deferred();
};
AjaxCall.prototype.resolve = function () {
this.dfr.resolve(
this.xhrResponse.result,
this.xhrResponse.jqXHR
);
this.resetDfr();
};
AjaxCall.prototype.reject = function () {
this.dfr.reject(
this.xhrResponse.jqXHR
);
this.resetDfr();
};
AjaxCall.prototype.query = function () {
var _this = this;
// if query hasn't run yet, or didn't return success, run it again
if (_this.status != 'OK') {
$.ajax.apply(_this, _this.args)
.done(function (result, textStatus, jqXHR) {
_this.xhrResponse.result = result;
_this.xhrResponse.jqXHR = jqXHR;
_this.resolve();
})
.fail(function (jqXHR) {
_this.xhrResponse.jqXHR = jqXHR;
_this.reject();
})
.always(function (a, b, c) {
var statusCode = (typeof c !== 'string'
? c
: a).status;
if (statusCode == 200) {
_this.status = 'OK';
}
});
}
// if query has been run successfully before, just skip to next
else {
_this.resolve();
}
return _this.dfr.promise();
};
AjaxCall class is as provided above, and I make the three consecutive calls like this:
var First = new AjaxCall('/'),
Second = new AjaxCall('/asd'),
Third = new AjaxCall('/qqq');
First.then(function () {
console.log('#1 done');
}, function() {
console.error('#1 fail');
});
Second.then(function () {
console.log('#2 done');
}, function() {
console.error('#2 fail');
});
Third.then(function () {
console.log('#3 done');
}, function() {
console.error('#3 fail');
});
var toRun = function () {
First.query()
.then(function () {
return Second.query();
})
.then(function () {
return Third.query()
});
};
$('button').click(function () {
toRun();
});
Those code are in a testing environment. And by testing environment, I mean a simple HTML page and basic server support for debugging.
Home page (/) always returns 200 Success.
/asd returns 404 Not Found for the first 3 times and 200 Success once as a pattern (i.e. three 404s -> one 200 -> three 404s -> one 200 -> three 404s -> ... ).
/qqq returns 404 Not Found all the time.
When I click the only button on the page, first query returns success and second fails as expected. When I click the button second time, first query skips because it was successful last time and second fails again, also as expected.
The problem here is:
before I used the resetDfr method because the dfr is alreay resolved or rejected, it doesn't react to resolve and reject methods anymore.
When I call the resetDfr method in the way I show in the example, dfr is able to get resolved or rejected again, but the callbacks of the old dfr are not binded with the new dfr object and I couldn't find a way to clone the old callbacks into the new dfr.
What would be your suggestion to accomplish what I'm trying to do here?
Promises represent a single value bound by time. You can't conceptually "reuse" a deferred or reset it - once it transitions it sticks. There are constructs that generalize promises to multiple values (like observables) but those are more complicated in this case - it's probably better to just use one deferred per request.
jQuery's AJAX already provides a promise interface. Your code is mostly redundant - you can and should consider using the existent tooling.
Let's look at $.get:
It already returns a promise so you don't need to create your own deferred.
It already uses the browser cache, unless your server prohibits HTTP caching or the browser refuses it only one request will be made to the server after a correct response arrived (assuming you did not explicitly pass {cache: false} to its parameters.
If making post requests you can use $.post or more generally $.ajax for arbitrary options.
This is how your code would roughly look like:
$("button").click(function(){
var first = $.get("/");
var second = first.then(function(){
return $.get("/asd");
});
var third = second.then(function(){
return $.get("/qqq");
});
});
The reason I put them in variables is so that you will be able to unwrap the result yourself later by doing first.then etc. It's quite possible to do this in a single chain too (but you lose access to previous values if you don't explicitly save them.
For the record - it wasn't a stupid question at all :)

jQuery $.Deferred() with Dynamic Url

I am starting to re-factor some code to use promises and caching to ensure cleaner code. My Code (See below) has tried to use this new premise and is working with slight success. The main issue is that the caching mechanism that is uses by default prevents my from being able to "pass" in a dynamic url value without it returning the same cached results every time. How can i update the code below to use a cache key?
"use strict";
var FLEX = window.FLEX|| {};
FLEX.Following = FLEX.Following|| {};
FLEX.Following.Process = function () {
var deferred = $.Deferred(),
execute = function (followUrl) {
$.ajax(
{
url: _spPageContextInfo.webAbsoluteUrl +
followUrl,
method: "GET",
headers: {
"accept": "application/json;odata=verbose",
},
success: function (data) {
deferred.resolve(data);
},
error: function (err) {
deferred.reject(err);
}
}
);
return deferred;
};
return {
execute: execute
}
}();
FLEX.Following.Init = function (divName, followUrl) {
FLEX.Following.Process.execute(followUrl).promise().then(
//success
function (data) {
var html = "";
$.each(data.d.Followed.results,function(index, value){
html += value.Name + "<br />";
});
$(divName).html(html);
},
//failure
function(err) {
$(divName).html('Failed');
}
);
}
I then call the code using the following lines:
$(document).ready(function() {
FLEX.Following.Init("#followed-sites", "/_api/social.following/my/followed(types=4)");
FLEX.Following.Init("#followed-people", "/_api/social.following/my/followed(types=1)");
FLEX.Following.Init("#followed-documents", "/_api/social.following/my/followed(types=2)");
});
The resulting output "works" however i get the same results in each div as it appears that every subsequent call jQuery gets the cached version from one of them and therefore each result set is identical. How can i ensure that i can use the same functions with the different dynamic urls as above and cache them separately.
Thanks
I think the root problem is that you create a single $.Deferred() and then you try to reuse it for multiple calls to execute() and that won't ever work. A deferred is a one-shot trigger. Once it has been fulfilled or rejected it stays that way and contains the same data forever. If you want a different result on subsequent calls, you have to create a new deferred for each call. So, because you were creating your deferred outside of the execute() function, you only ever had one deferred and thus the first result would just stick forever.
The fix here is to use a separate promise/deferred for each call to execute(). Since $.ajax() already returns a new promise each time you call it - you can use that instead of creating your own $.Deferred manually which is an anti-pattern. Let's refactor and solve both issues at once.
Here's your function refactored so solve those issues:
var execute = function (followUrl) {
return $.ajax({
url: _spPageContextInfo.webAbsoluteUrl + followUrl,
method: "GET",
headers: {"accept": "application/json;odata=verbose"}
});
};
Then, in this line of code:
FLEX.Following.Process.execute(followUrl).promise().then(
You should remove the .promise() because my change above is already returning a promise so this line can just become this:
FLEX.Following.Process.execute(followUrl).then(

How to use jQuery ajax data to variable

I have the following javascript code:
function initSite(){
var site;
$.getJSON(www+'init/initSite', function(data) { site = data; });
}
$(document).ready(function(){
var site = initSite();
console.log(site);
}
which returns undefined... how can i store the json object that i recieve in the site variable so i can use it later?
EDIT:
This seem to work but im not sure if its correct to use this solution
var site = null;
$.ajax({
url: www+"init/initSite",
async: false,
dataType: 'json',
success: function (data) {
site = data;
}
});
console.log(site);
of course you got undefined because your function doesn't return anything and the ajax call is also asynchronous, so you have to wait the server response. Since $.ajax (and shortcuts) returns a promise you can do this task using deferred
function initSite(){
return $.getJSON(www+'init/initSite');
}
$(document).ready(function(){
$.when(initSite()).done(function(data) {
/* continue here the code execution, e.g. call another function */
doAllTheRemainingWorkWith(data)
});
}
as you can see this code is short and easy to read
function initSite(onSuccess){
$.getJSON(www+'init/initSite', onSuccess);
}
$(document).ready(function(){
initSite(function(data){
var site = data;
// initialize your code.
});
}
The problem is just a miss concept:
getJSON is an async call, and the site = data; will only happen way after the DOM is ready.
in order for you to make everything work the way it should, your initialization needs to start from your async call result and never before, for example:
// no need to wait for DOM ready to call `initSite`
initSite();
function initSite() {
$.getJSON(www+'init/initSite', function(data) {
initialization(data);
});
}
function initialization(site) {
// initialize all the things that need to be done
console.log(site);
}
$(document).ready(function(){
// do other stuff, for example show a loading message/image
}

Categories