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.
Related
I realise this is more of a general question, but I've read through similar answers on here but I can't find more of an overview. I'm new to callbacks and I'm trying to understand when they should be used.
The MDN web docs has this example;
function greeting(name) {
alert('Hello ' + name);
}
function processUserInput(callback) {
var name = prompt('Please enter your name.');
callback(name);
}
processUserInput(greeting);
However I'm struggling to see how this is more beneficial than the following, where I'm not passing the greeting function as a parameter?
function greeting(name) {
alert('Hello ' + name);
}
function processUserInput() {
var name = prompt('Please enter your name.');
greeting(name);
}
processUserInput();
As Javascript is async, sometimes it is difficult to handle response from non-blocking functions, for ex. if you are making an ajax call then it'll be executed asynchronously and results will be returned sometime later, by that time the main execution flow will pass the ajax code and starts executing following statements, in that case, its very difficult to catch the response to process further.
To handle those cases, callbacks comes into picture where you pass a function as the parameter to the ajax function and once the response is returned then call the callback by passing response data as a parameter to process further.
more info here http://callbackhell.com/
In simple terms you can say a callback is a way of asking a question (or requesting a task) in advance, i.e. when you're done with this do this (usually with the result). The whole point is to set aside functions that are to be done later, usually because you don't have the required inputs to do them now.
The 2 main differences between your implementation and the MDN one is that yours is harder to maintain and harder to reason about hence test.
1. Maintanance / Reusability
Imagine you're a few thousand lines of code into a code base then you're required to change what processUserInput() does. Its much easier to change or write a new callback function instead of changing the function processUserInput(). This would be evident if processUserInput was a bit more complicated. This also means the MDN one is much more useful in various scenarios unlike your implementation. You can reuse it in different situations like saying good bye, capitalizing names etc simply by writing different callbacks to plug into processUserInput().
2. Testing / Easier to reason about
The MDN implementation is much more easier to understand. Its easier to assume that the function processUserInput(greeting) will probably return a greeting than it is to assume what processUserInput() does. This makes it easier to test because you can always be sure the MDN implementation will always return the same output given an input.
Callbacks can be extremely useful depending on the circumstances; for example, when working with JavaScript for Google Chrome browser extension development, a callback can be used for intercepring web requests once it has been setup.
The purpose of a callback in general is to have the callback routine executed upon a trigger - the trigger being an event of some kind. Usually, functionality follows an interface of chained APIs. By implementing callback support, you can redirect execution flow during a stage of an operation. Callbacks are especially useful to third-party developers when dealing with someone elses library depending on what they are trying to do. Think of them like a notification system.
Functions in general taking in parameters is useful for flexibility and maintenance. If you use different functions for different things, the functions can be simply re-used over and over again to provide different functionality - whilst still preventing bloating the source code with more-or-less the same code over and over again. At the same time, if you use functions to your own library and a bug shows up, you can simply patch it for the one function and then it will be solved.
In your example, your passing a callback routine to the function you're calling - the function you're calling will call the callback function and pass the correct parameters. This is flexible because it allows you to have a callback routine called for printing the contents of the variable, and another for calculating the length of the string passed in, or another for logging it somewhere, etc. It allows you to re-use the function you setup, and have a different function called with the correct parameters without re-making the original function.
This example is not appropriate for understanding callbacks
In simple Language callbacks functions are used when we have to do some stuff after or in response of some other event or function or expression.
i.e when the parent function completes its execution then callback gets executed.
simple Example
function hungerStatus(status,cb){
return cb(status)
}
function whatToDo(status){
return status ? "order Pizza" : "lets play"
}
hungerStatus(false,whatToDo)
Another example
// global variable
var allUserData = [];
// generic logStuff function that prints to console
function logStuff (userData) {
if ( typeof userData === "string")
{
console.log(userData);
}
else if ( typeof userData === "object")
{
for (var item in userData) {
console.log(item + ": " + userData[item]);
}
}
}
// A function that takes two parameters, the last one a callback function
function getInput (options, callback) {
allUserData.push (options);
callback (options);
}
// When we call the getInput function, we pass logStuff as a parameter.
// So logStuff will be the function that will called back (or executed) inside the getInput function
getInput ({name:"Rich", speciality:"JavaScript"}, logStuff);
refer callback exaplanation
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();
I want to package a ajax call into an interface without then.
If i do like this, it will just return 'No ajax return';
var ajaxReturn = ajaxFunction();
function ajaxFunction(){
var text = 'No ajax return';
// get fileName using an ajax get
$.ajax();
return text;
}
If i do like this, it will be ugly for using then;
function ajaxFunction(){
var text = 'No ajax';
var dtd = $.Deferred();
$.ajax();
return dtd.promise();
}
$.when(ajaxFunction()).then();
I just want the interface to be simple and return the right thing, can i?
//return the right
var ajaxReturn = ajaxFunction();
function ajaxFunction(){
var text = 'No ajax';
var dtd = $.Deferred();
$.ajax();
return dtd.promise();
}
$.when(ajaxFunction()).then();
Whoa, what is all that? You do need .then but you don't need most of the surrounding stuff. $.ajax generates a promise for you. You don't need to make a promise object yourself. In fact, often the only reason you need to manually set up a Deferred/Promise directly is if you're using some library that sets up callbacks and doesn't use promises itself.
function ajaxFunction(){
return $.ajax();
}
ajaxFunction().then(function(data) { ... });
Now, let's say that you didn't actually want to return the JSON structure on the end of the ajax function; you want to take out just a number from inside of it, or tweak one value to make it an easier-to-use function for its callers. Easy enough:
function ajaxFunction(){
return $.ajax().then(function(data) {
return data[12].number;
}
}
ajaxFunction().then(function(number) { ... });
In direct answer to your question: No, what you asked for isn't possible. Whenever your JavaScript methods are running, the browser can't process other events like clicks and even basic scroll operations. So, any long-running operations (like contacting the server) do not return straight away, and instead offer a callback operation.
Well..., ajax is asynchronous so you either use .then() or use a callback logic... Doing synchronous ajax is not a option for me, so I won't even mention it.
The alternative to .then() would be something like this:
ajaxFunction(function(res){ // pass a function into it
// this will be called when the ajax is done
alert(res);
});
function ajaxFunction(callback){
// get fileName using an ajax get
$.ajax({
success: callback
});
}
But again, maybe you can use just a normal ajax callback pattern anyway
$.ajax({
...
success: function(res){
// use the res
}
});
Ajax is asynchronous. then is designed to make writing async operations look more similar to synchronous code and can actually be very elegant.
Additionally, $.ajax() returns a promise and is well suited to be written as follows:
function ajaxFunction(){
return $.ajax();
}
ajaxFunction().then(function(response){
// do whatever you want with the response
})
You simply can't write asynchronous code that way (ajaxResult = ajaxFunction()). The interpreter is going to keep trucking along line by line and ajaxResult will not be ready in time.
Read up on chaining $.Deferred's. It will really clean up your async code.
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.
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);
}
};