How to 'queue' piece of code in javascript - javascript

I've made a class that, when initialized, starts to download a JSON file.
However, the downloading runs asynchronously.
So after I declare it I start working with it, but it fails because it hasn't loaded the file yet.
I could turn async off, but is there another way without freezing the browser?
I'm currently working around it with a setTimeout option, but that seems like an ugly hack.
var d;
$(document).ready(function() {
d = new Duk('element');
d.getBlueprint('hud.json');
setTimeout(start, '2000');
});
function start(){
test = new d.Dialog(d.blueprint.screens.test);
test.draw();
}

You have to attach event handler to the object load completion event. The library you are working with must supply this event. I don't know what a Duk is, or what getBlueprint() does, but you should check the documentation for whatever that class and method is, and see if there is an oncomplete callback. If there is, you'd do something like this:
$(document).ready(function() {
d = new Duk('element');
d.getBlueprint('hud.json', {
onComplete: function() {
test = new d.Dialog(d.blueprint.screens.test);
test.draw();
}
});
});
Obviously, I just made that up. I don't know if your library has an onComplete method defined like this, but I hope you see what I mean. Without knowing more about the library you are using, I can't give a better example.

Timeouts are not the way to solve this problem.
You want to set up a callback function alongside the request so that the object handling the request knows what to do when the response comes in.
It is unclear from your code what framework you're using (I can't make any sense of getBlueprint, which seems to be the call that initializes the remote request), so if you could provide more information on this, we can provide more customized help.

You want your getBlueprint() method to be able to accept a callback which is run when the file is finished downloading.
function getBlueprint(file, callback)
{
// do whatever to get the file
// ...
// trigger the callback
callback();
}
$(document).ready(function() {
var d;
function start(){
test = new d.Dialog(d.blueprint.screens.test);
test.draw();
}
d = new Duk('element');
d.getBlueprint('hud.json', start);
setTimeout(start, '2000');
});

I agree that a callback function is the proper way. If this is code you cannot modify, you can do something like this, but seriously, use a callback!
function wait_for_load(expr, func)
{
var interval = window.setInterval(function()
{
if(eval(expr))
{
window.clearInterval(interval);
func();
}
}, 20);
}
var d;
$(document).ready(function() {
d = new Duk('element');
d.getBlueprint('hud.json');
wait_for_load('d.blueprint', start);
});

Related

Calling function in a callback before defining it in javascript?

I am having some trouble wrapping my head around this. I have a web application that is almost entirely built with javascript. It starts out with a basic template, then starts adding content to it as the user interacts. I am trying to use Greensock as the animation library which has the ability to use a progress slider to show how far you are in the animation, see the second box here: https://greensock.com/timelinemax
The issue is that it uses a callback onUpdate that is supposed to run that function on each frame. Then I can use it to make the slider track with the animation.
var mainTL = new TimelineLite({onUpdate:updateSlider});
function updateSlider() {
sliderTimeline.noUiSlider.set( mainTL.progress());
}
This would work - except that the slider object doesn't exist yet. I don't know why, this is some of the last code to be included in the file, but I get a couple errors in the console log just loading the page `ReferenceError: sliderTimeline is not defined' but then everything works.
To try getting away from those errors, I tried to do it like this:
var mainTL = new TimelineLite({onUpdate:updateSlider});
$( document ).ready(function() {
function updateSlider() {
sliderTimeline.noUiSlider.set( mainTL.progress());
}
});
except now it fails because the updateSlider' function hasn't been defined, and it fails to start at all. I could put them both in a$( document ).ready(function()`, but then they become local functions / variables and the 5 other javascript files I am working with don't have access to them.
Do I have to live with the errors, or is there something I am not thinking of?
You can check whether sliderTimeline exists before trying to call it. For example change function updateSlider() to:
function updateSlider() {
if (typeof sliderTimeline !== 'undefined') {
sliderTimeline.noUiSlider.set( mainTL.progress());
}
}
Or if you know that sliderTimeline is declared, but not assigned yet:
function updateSlider() {
if (sliderTimeline) {
sliderTimeline.noUiSlider.set( mainTL.progress());
}
}
Note that this works because onUpdate is called frequently, so it will eventually be called when sliderTimeline is eventually defined.
Edit:
Additionally, you can assign global variables inside $( document ).ready() as long as you declare them outside of the function.
For example:
var mainTL;
var updateSlider;
$( document ).ready(function() {
updateSlider = function () {
sliderTimeline.noUiSlider.set( mainTL.progress());
};
mainTL = new TimelineLite({onUpdate: updateSlider});
});
If you look at their codepen page http://codepen.io/GreenSock/pen/FnsqC/ they have:
var tl = new TimelineMax({delay:0.5, repeat:3,
repeatDelay:2, onUpdate:updateStats,
onRepeat:updateReps, onComplete:restart});
function updateReps() {
reps++;
repeatCount.innerHTML = reps;
}
function updateStats() {
time.innerHTML = tl.time().toFixed(2);
totalTime.innerHTML = tl.totalTime().toFixed(2);
progress.innerHTML = tl.progress().toFixed(2);
totalProgress.innerHTML = tl.totalProgress().toFixed(2);
}
Meaning that you need to define the callback function of onUpdate.

IE11 script onload not working (sometimes)

I am currently working on a data-intensive web application that frequently communicates with an external API and retrieves JSONP data when returned. The script depends upon a library called head.js v1.0.3. http://headjs.com/ to accomplish this. However, I noticed that in IE 11 for some reason, the onload event for the script sometimes, but not always, fires before the script has actually loaded into the browser. The behavior is demonstrable whether using head.js or not. Alternatively, I may create a script element with the onload event set to capture the returned data. Sometimes it works, and sometimes not. Even more weird is that once it happens the first time, it seems to keep happening for the duration of the browser session.
Any ideas for a workaround?
Here is some example code:
//somejson.js
/*
window["queryResult"] = {blah:'blah'}
*/
function loadScript() {
head.load("/somejson.js", afterScriptLoad)
}
function afterScriptLoad() {
var result = queryResult
//Throws error because window.queryResult is sometimes undefined
}
After a little bit of research, it seems the only way around this bug is to modify the API so that once the variable holding the JSONP is initialized, the script itself triggers the callback. Unfortunately, this would not work as a solution for others if they do not have access to modify whatever API is in use, but it does solve the problem for me.
//somejson.js
/*
window["queryResult"] = {blah:'blah'}; scriptCallback()
*/
function loadScript(callback) {
var c = new afterScriptLoad(callback)
window["scriptCallback"] = c
head.load("/somejson.js", c)
}
function afterScriptLoad(callback) {
var retrieved = false
return function () {
if (!retrieved) {
retrieved = true
callback(queryResult)
}
}
}
function myCallback(response) {
//do something
}

How can I call a JavaScript function after every a4j AJAX response?

I am working on a web app using JSF w/Seam. I want to be able to call a JavaScript function after every ajax response. I'm looking for a way to do this without putting an oncomplete attribute on every commandLink/commandButton on every page.
I think there's a way to set up a servlet filter (interceptor? I get the terms confused) to inject the JS call into each response. I'm going to look into that. In the meantime, if anyone has any other suggestions, I'm all ears.
EDIT: I think the jQuery ajaxSuccess method might be the way to go here, but I'm not sure how to actually use it. I can't get anything to register. I basically want to add code to get any and all ajax requests from any source to call my JavaScript method on success. Can anyone show me the proper way to do this? I've tried a number of ways to do this, including adding jQuery("*").ajaxSuccess(function(){myFunction();}); to the bottom of my template xhtml file.
Rewritten answer: see original answer in revision history
You could override the default send method of XMLHttpRequest with one that hijacks the readystatechange handler:
(function ()
{
var xhrSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function ()
{
var handler = this.onreadystatechange;
this.onreadystatechange = function ()
{
if (handler) {
if (handler.handleEvent) handler.handleEvent.apply(xhr, arguments);
else handler.apply(xhr, arguments);
}
if (this.readyState == 4)
{
// your oncomplete function here
this.onreadystatechange = handler;
}
};
xhrSend.apply(this, arguments);
};
})();
Edit: The above function doesn't work with jQuery requests, and so potentially it could fail with other libraries as well. The revision below addresses the issue with a setTimeout hack to delay the code that overrides the handler. Of course, with jQuery, you can just use the .ajaxSuccess() global handler, but for other libraries with similar behavior, this would be useful.
(function() {
function globalHandler() {
if (this.readyState == 4) {
// your oncomplete code here
}
}
var xhrSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
var xhr = this;
if (xhr.addEventListener) {
xhr.removeEventListener("readystatechange", globalHandler);
xhr.addEventListener("readystatechange", globalHandler, false);
}
else {
function readyStateChange() {
if (handler) {
if (handler.handleEvent)
handler.handleEvent.apply(xhr, arguments);
else
handler.apply(xhr, arguments);
}
globalHandler.apply(xhr, arguments);
setReadyStateChange();
}
function setReadyStateChange() {
setTimeout(function() {
if (xhr.onreadystatechange != readyStateChange) {
handler = xhr.onreadystatechange;
xhr.onreadystatechange = readyStateChange;
}
}, 1);
}
var handler;
setReadyStateChange();
}
xhrSend.apply(xhr, arguments);
};
})();
http://jsfiddle.net/gilly3/FuacA/5/
I tested this in IE7-9, and the latest versions of Chrome and FF
Since you are using RichFaces you can simply use this:
<a:status id="globalStatus" onstart="onRequestStart()" onstop="onRequestEnd()" />
Using a4j:status should work, but it has to be inside an h:form tag:
<h:form id="randomForm" styleClass="edit">
<a:status id="stateStatus"
onstart="Richfaces.showModalPanel('waitBx'),document.getElementById('randomForm:search').disabled=true;"
onstop="Richfaces.hideModalPanel('waitBx'),document.getElementById('randomForm:search').disabled=false;"
styleClass="message" >
</a:status>
...... way more code
</form>
After every ajax call this pops up a wait picture and disables the search button.
Interestingly enough, at least in our code, this doesn't work for anything in a nested a4j:region.
I think this is what you are looking for: Using Global Ajax Handlers In jQuery

How do i execute a callback in a jQuery Plugin code

This may sound really like a newbie .. But i used the jQuery Boilerplate on this page - http://stefangabos.ro/jquery/jquery-plugin-boilerplate-revisited/ and created a plugin. Everything works fine, except now i want to add a callback. I want to execute this -
$.Alerter({'message':'this is a test','onSuccess':function(data) { alert(data); } });
The onSuccess is a callback function which is added to the defaults.
My question is – how do i send the output to the onSuccess. I want it to return back a TRUE or FALSE value after certain steps have been executed in the init()
Something like this:
plugin.result = null;
plugin.init = function() {
// do stuff
...
// save _result in public variable result
plugin.result = _result;
}
If you are writing this plugin for dom operations, you could also use it like plugin.data('result',_result);
Since I don't know anything else I can't give further insight.
Hope this will help you.

Add console.profile statements to JavaScript/jQuery code on the fly

We have a thick client app using jQuery heavily and want to profile the performance of the code using firebug's console.profile API. The problem is, I don't want to change the code to write the profile statements. Take this example:
var search=function(){
this.init=function(){
console.log('init');
}
this.ajax=function(){
console.log('ajax');
//make ajax call using $.ajax and do some DOM manipulations here..
}
this.cache=function(){
console.log('cache');
}
}
var instance=new search();
instance.ajax();
I want to profile my instance.ajax method, but I dont want to add profile statements in the code, as that makes it difficult to maintain the code.
I'm trying to override the methods using closures, like this: http://www.novogeek.com/post/2010/02/27/Overriding-jQueryJavaScript-functions-using-closures.aspx but am not very sure how I can achieve. Any pointers on this? I think this would help many big projects to profile the code easily without a big change in code.
Here is the idea. Just run the below code in firebug console, to know what I'm trying to achieve.
var search=function(){
this.init=function(){
console.log('init');
}
this.ajax=function(){
console.log('ajax');
//make ajax call using $.ajax and do some DOM manipulations here..
}
this.cache=function(){
console.log('cache');
}
}
var instance=new search();
$.each(instance, function(functionName, functionBody){
(function(){
var dup=functionBody
functionBody=function(){
console.log('modifying the old function: ',functionName);
console.profile(functionName);
dup.apply(this,arguments);
console.profileEnd(functionName);
}
})();
console.log(functionName, '::', functionBody());
});
Now what I need is, if i say instance.ajax(), I want the new ajax() method to be called, along with the console.profile statements. Hope I'm clear with the requirement. Please improvise the above code.
Regards,
Krishna,
http://www.novogeek.com
If you only want to modify the single instance of "search" then this should work:
$.each(instance, function(name, method){
if (typeof method !== 'function') return;
instance[name] = function() {
console.profile(name);
var ret = method.apply(this, arguments);
console.profileEnd(name);
return ret;
};
});
I know this is from a long time ago but I wanted to add this in case other people find this answer. You can make anonymous/private functions work by adding a name to each one. The other comments mention doing it manually bit I wanted to explain how to:
$('.stuff').each(function() { ... });
to
$('.stuff').each(function workOnStuff() { ... });

Categories

Resources