So I've got these functions:
function UrlExists(url){
$.ajax({
url: url,
success: function(data){
alert('exists');
},
error: function(data){
alert('fail');
}
});
}
function addScript(filepath, callback){
if (filepath) {
var fileref = document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src", filepath);
if (typeof fileref!="undefined")
document.getElementsByTagName("head")[0].appendChild(fileref);
}
if (callback) {
callback();
}
}
And then in my $(document).ready() I've got a bunch of these:
addScript(roofPathMrtu.js);
addScript(roofPathTrtu.js);
addScript(lowerPathMrtu.js);
etc...
Which I then need to check if they were successfully loaded or not, so I call:
UrlExists('roofPathMrtu.js');
The problem is that this UrlExists function is not working, and I think it's because it is running before all the addScript functions are done.
How can I have my UrlExists function run only after all the addScript functions are done? I was going to use the callback parameter of the addScript function on the last one, but I don't think that is gonna work.
A way that I have been doing this is not to use the javascript method of setimeout(), but using the jquery feature when. IF not, then I would use a Que. The syntax is
$.when(function()).then(fucntion2());
or
$.when(function1()).done(function2());
You could overlap these if you wanted to, but it is not best when considering both elegant and efficiency in code. Using the que would probably be the next step, using $.when will not accomplish what you want.
http://api.jquery.com/jQuery.when/
Your addScript() function is inserting the tag into the dom and returns immediately. At that point, the browser still needs to fetch the javascript file specified in the src attribute. I suppose that UrlExists() is being called after the execution of the addScript() functions but before the browser has a chance to fetch the javascript files.
use a $.Deferred object to "listen" to the various done events. You might want to combine a few $.Deferred object and use the $.when function to listen for multiple resolved promises
http://thiswildorchid.com/jquery-progress-and-promises check out this link it might help. But it sounds like you might need this. It helps a lot with async functions and you should see if it is a good fit for you.
What you want to do is define an onload function on the script element. It's not hard, but the implementation starts to look ugly. For the particular problem you're dealing with, I would recommend you look at Require JS.
Related
Before anyone marks it as duplicate, this post does not actually answer the question but suggests a different way altogether to solve that particular issue.
Mine is a different issue. Please let me explain.
In my case, there are various .js files (plugins) which are being loaded with jquery getscript and stored in variables. Then whenever required they will be executed (more than once)
The code for loading script (this code will only run once at the init of the system for each plugin js file)
var storedFunc;
$.getScript(pathToPluginJSFile, function( data, textStatus, jqxhr ) {
storedFunc = data;
});
All the plugins are in this format
(function(){
//lots of code here
})()
But when I checked the storedFunc variable in console, I found out that it has been stored as String variable. Like this,
"(function(){
//lots of code here
})()"
Now to execute this, I used eval, like this (this code can be executed multiple times based on the need)
eval(storedFunc)
Everything is working fine and i am happy with it, but here comes the problem, I read in somewhere that the usage of eval is kind of like a bad thing to do. So now I am afraid that thought everything is working fine, all these negativity of using eval spread on the internet might scare my client away. :(
So, please tell me how I can run that stored function (which has become a string) without using eval.
Or should I use anything else than $.getScript which does not convert a function into a string ?
Or if there is any other way altogether rewriting this plugin functionality?
Please show me the way. I am in need of this solution badly.
Any help will be appreciated.
Thanks in advance.
Understanding how $.getScript works
Seems there is some confusion on how $.getScript works. If you notice jQuery's documentation on the method, and as #Pointy made mention of in the comments, this is stated:
Load a JavaScript file from the server using a GET HTTP request, then execute it.
Here's an example: Let's pretend the contents of the file being returned is only this:
// Contents of yourExternalFile.js
console.log('Executed!');
Now, when you use $.getScript:
$.getScript(pathToPluginJSFile, function( data, textStatus, jqxhr ) {
// The script you retrieved has already executed at this point, and you will find "Executed!" in the console.
console.log('All Done');
});
Console output:
> Executed!
> All Done
The $.getScript method is not meant to be used to return a string of the content of the file. However, while that data is available in the callback, the contents of the file have already been executed. So by taking the string version of the file, and re-executing it with either new Function, or even eval, you are executing it twice on the page (jQuery does it once, and so do you).
Original Post:
Use the Function constructor instead of using eval.
// Your function as a string stored to a variable
var stringFunction = "(function(){console.log('Executed');})()";
// Use the Function constructor to create a new function:
var executableFunction = new Function(stringFunction);
// Now you can execute it
executableFunction(); // logs "Executed"
This snippet from this SO question/answer addresses the difference between eval and new Function.
eval() evaluates a string as a JavaScript expression within the current execution scope and can access local variables.
new Function() parses the JavaScript code stored in a string into a function object, which can then be called. It cannot access local variables because the code runs in a separate scope.
Additional Information (Based on comments)
Yes, you can just get the string contents of the file and store them to a variable without the contents of the file executing. You can have that function to execute anytime. You just need to use the regular get method using jquery, and set the dataType to text. This way, the script will not execute, and you can execute it as you see fit:
var storedFunction;
$.get({url: pathToPluginJSFile, dataType: 'text'})
.done(function (data) {
// Turn the script into a new function and store it
// The information in the script file has not done anything yet
storedFunction = new Function(data);
})
.fail(function () {
console.log('Failed :(');
});
The only thing you will have to watch out for, is making sure that the function was assigned to the storedFunction variable as you are making an api call, and you have to wait for that to finish before attempting to make the function execute.
// Later on, call that function anytime, and as often as you want:
storedFunction();
Will it be a good or a bad practice to override all ajax call in my Web App's JavaScript code with an "invisible" Cache layer?
It'd be something like (pseudo-code):
$.prototype.ajax = function(...) {
if( requested URL can be found in localStorage )
return dataFromLocalStorage; // as Deferred
data = invoke original $.ajax;
localStorage.setItem(key, data);
return data; // as Deferred
}
I can think of one pro: no refactoring is needed for existing ajax calls, and one con: future developers will be unaware of this functionality, as it disguises itself as a regular ajax call.
What do you think? Will it be a good practice or should I avoid it?
No, it is not a good idea to override the default behavior of $.ajax like this. Other code on your page including jQuery plugins might depend on the default behavior.
It is likely at some point you might want to get the freshest version of some data, if $.ajax always caches to localStorage there will be no way to do that. This could also lead to future debugging headaches when someone working on the code (including you) can't figure out why their AJAX calls keep returning stale data.
It would be much better to just implement the caching in a separate function. That way when you see a call to something like ajaxWithCaching it will be obvious that something more that a plain AJAX call is going on.
I wouldn't be overriding the default behavior of anything! If you change the way $.ajax works, what will you do when you're using a lightbox plugin (or literally anything) which relies on the normal functionality of $.ajax?
It would backfire terribly upon you.
Instead, create your own function which performs the caching, and uses $.ajax normally.
var cache={}; // Your Cache
function getArticle(id,callback){ // Your caching ajax-wrapping function (requires a callback)
// Require the arguments
if (arguments.length<2) throw "getArticle requires id and callback arguments";
// If the article is cached, pass it to the callback and return
var cachedArticle = id in cache
? cache[id]
: null;
if (cachedArticle) return callback(cachedArticle,true); // cached=true
// If that article isn't in the cache, perform an ajax call to get it
$.ajax({
url: "article.php",
success: function(data,textStatus,jqXHR){
cache[id] = data; // Cache it!
callback(data,false); // Pass it to the callback // cached=false
}
});
return true; // reporting that we launched an ajax request
}
Then to use it, you've got a pretty robust little function.
var ajaxWasUsed = getArticle(8, function(articleData,wasCached){
console.log("Got Article #8 "+(wasCached?"(from cache)":"(fresh ajax)")+":",articleData);
});
Since I'm using this type of call often I wish to make this reusable:
function getJSON(cmd){
$.getJSON(cmd, function(data) {
}).done(function(data) {
return data;
}).fail(function() { console.log('Server failed!') });
}
I hoped to use it like this:
function susbcribe(id){
var cmd = 'subscribe.php?='+id;
var obj = getJSON(cmd);
console.log(obj);
}
But javascript runs console.log before the async json can even return anything to obj.
Just to be clear - i know i can execute code inside of .done(), but because I use this often I wish to forgo rewriting the same function over and over.
So the question is: is there a way to make js stop and wait for getJSON to finish and return something to obj ?
As things currently stand, you will have to at least write the done function every time. You can't escape callback hell by pretending it doesn't exist.
There are ways to avoid some of it by using promises cleverly, but for simple things, this is pretty much as simple as it gets. Until we get support for generators/iterators some time in 2025.
You could set the fail function as a global "ajax event" handler to avoid having to type error handling every time.
I'm trying to build an API in JS that will perform some operations and then execute the callback that's registered in AS when it's done. Because it's an API, I am just providing a JS method signature for another developer to call in Flash. Thus, the callback name that's registered in the AS part of the code should be a parameter that's passed in to the JS API in order for JS to communicate back to Flash.
For example:
[AS3 code]
ExternalInterface.addCallback("flashCallbackName", processRequest);
ExternalInterface.call("namespace.jsFnToCall", flashCallbackName);
function processRequest(data:String):void
{
//do stuff
}
[JS code]
var namespace =
{
jsFnToCall: function(callback)
{
//Do stuff in this function and then fire the callback when done.
//getFlashMovie is just a util function that grabs the
//Flash element via the DOM; assume "flash_id"'s a global var
//Below does not work...it's what I'd be ideally be doing some how.
getFlashMovie(flash_id).callback(data);
}
};
Because the definition of the function is in AS, I can't use the window[function name] approach. The only way I can think of is to build the callback in a string and then use the eval() to execute it.
Suggestions? T.I.A.
Well, I can think of one thing I would try, and one thing that would work.
What I would try first.
getFlashMovie(flash_id)['callback'](data);
What would work: Have the callback always be the same, say callback. The first parameter to the callback could be used to determine what actual function to call in flash. For example:
function callback($fn:String, $data:*) {
// either
this[$fn]($data);
// or
switch ($fn) {
case "callback1":
DoSomeCallback($data);
break;
}
Additionally passing the objectID makes it a bit simpler:
ExternalInterface.addCallback("flashCallbackName", processRequest);
ExternalInterface.call("namespace.jsFnToCall", ExternalInterface.objectID, "flashCallbackName");
Then in your JS:
var namespace =
{
jsFnToCall: function(objectID, callback)
{
//Do stuff in this function and then fire the callback when done.
document[objectID][callback](data);
}
};
I have a question, I think the answer will be simple, but I can't really find it ...
I have a function that creates content with ajax. After the function is finished I want to do something with the created content. To do that, I need to wait until all content is created, before I can do something with it.
What I prefer is something like this:
viewAllAccounts(function() {
//do something
});
or
viewAllAccounts().queue(function() {
// do something
});
But offcourse this is not going to work :)
I don't want to touch the viewAllAccounts function, because it is used multiple times in my app.
Is there a simple way to do something after a function is finished, not altering the function itself?
Thanks in advance!
when you invoke ajax calls with jquery, there are a bunch of handlers that fire when the ajax call completes. You should hook into those. Take a look-see at
http://api.jquery.com/jQuery.ajax/
particularly the 'success' and 'error' properties. just define functions for those and they will get called when the request completes.
For if you do not want to waste hundreds of bytes and a bunch of milliseconds on jQuery*:
You can create a second function:
function viewAllAccountsWithFinishingFunction(func) {
viewAllAccounts();
return func();
}
Then call:
viewAllAccountsWithFinishingFunction(function() {
//do something
});
* I do like jQuery, but using it for just one out of hundreds of features it has, I don't find it necessary.
Check out jQuery deferred objects:
http://www.erichynds.com/jquery/using-deferreds-in-jquery/
http://api.jquery.com/category/deferred-object/