I'm trying to use a closure (I think that's what it is..), I'd just like to execute a function with a local variable at some point in the future, like this:
function boo() {
var message = 'hello!';
var grok = function() { alert(message); }
foo(grok);
}
function foo(myClosure) {
$.ajax({
timeout: 8000,
success: function(json) {
myClosure();
}
}
}
I could get around this by using global variables and such, but would rather use something like the above because it at least seems a bit cleaner. How (if possible) do you do this?
Thanks
----------- Update --------------------
Sorry I wasn't clear - I was wondering if this is the correct syntax for the closure, I tried it out and it seems ok. Thank you.
Your existing code looks perfectly fine except for that missing paren at the end. ;)
If you're looking to understand the concept of closures more deeply, think of it this way: whenever something in a closured language is defined, it maintains a reference to the local scope in which it was defined.
In the case of your code, the parameter to $.ajax() is a newly-created object ("{ timeout: 8000, etc. }"), which contains a newly-created function (the anonymous "success" function), which contains a reference to a local variable ("myClosure") in the same scope. When the "success" function finally runs, it will use that reference to the local scope to get at "myClosure", even if "foo()" ran a long time ago. The downside to this is that you can end up with a lot of unfreeable data tied up in closures -- the data won't be freed until all references to it have been removed.
In retrospect, I may have confused you more than helped you. Sorry if that's the case. :\
Unless you actually want to make an AJAX call, setTimeout might be more along the lines of what you are looking for:
function foo(myClosure) {
setTimeout(myClosure, 8000); // execute the supplied function after 8 seconds
}
If your question was more along the lines of "Am I creating a closure correctly?", then yes, your function boo is doing the right thing.
Is it what you want?
var boo = (function() {
var message = 'hello!';
return function() {
foo(function() {
alert(message);
});
};
})();
function foo(myClosure) {
$.ajax({
timeout: 8000,
success: function(json) {
myClosure();
}
}
}
or just
function boo() {
$.ajax({
timeout: 8000,
success: function(json) {
alert('hello!');
// do sth with json
// ...
}
}); // <- missed a paren
}
The example is too simple to know what you want btw.
Related
I am newbie in js and I want to override/overwrite some fullcalendar functions from another script (my-fullcalendar.js) to make some changes in it for myself. for example function names are :
formatRange and oldMomentFormat.
formatRange is accessible from this.$.fullCalendar.formatRange but oldMomentFormat is not accessible via this kind of chain. But even when I do something like this in my-fullcalendar.js:
;(function () {
function MyformatRange(date1, date2, formatStr, separator, isRTL) {
console.log( "MyformatRange");
//other parts is exactly the same
// ...
}
this.$.fullCalendar.formatRange=MyformatRange;
console.log(this);
})();
nothing happens because no log is generated and even line by line tracing does not pass from here. but when observing "this" in console log MyformatRange replaced by original formatRange.
another problem is how can I override/overwrite oldMomentFormat function which is not in window hierarchy to access (or I can not find it) ??
OK, let's simplify the problem. In essence, you have this situation:
var makeFunObject = function () {
var doSomething = function (msg) {
console.log(msg);
};
var haveFun = function () {
doSomething( "fun!");
};
return {
doSomething : doSomething,
haveFun : haveFun
};
};
In other words you have a function that is creating a closure. Inside that closure are two "private" functions, one of which calls the other. But both functions seem to be "exposed" in the returned object.
You write some code:
var myFunObject = makeFunObject();
myFunObject.haveFun(); // fun!
Yep, seems to work just fine. Now let's replace the doSomething function in that returned object and call haveFun again:
myFunObject.doSomething = function (msg) {
console.log("My new function: " + msg);
};
myFunObject.haveFun(); // fun! <== wait what?
But wait! The new replacement function is not being called! That's right: the haveFun function was expressly written to call the internal function. It in fact knows nothing about the exposed function in the object at all.
That's because you cannot replace the internal, private function in this way (you cannot replace it at all, in fact, not without altering the original code).
Now draw back to the FullCalendar code: you are replacing the external function in the object, but the internal function is the one that is called by every other function inside FullCalendar.
I realize this is an old question, but I was butting my head against this same problem when I wanted to override the getEventTimeText function.
I was able to accomplish this, from inside my own JS file, like so:
$.fullCalendar.Grid.mixin({
getEventTimeText: function (range, formatStr, displayEnd) {
//custom version of this function
}
});
So, in terms of the function you were trying to override, you should be able to do it with:
$.fullCalendar.View.mixin({
formatRange: function (range, formatStr, separator) {
//custom formatRange function
}
});
Note: Make sure this runs before where you actually create the calendar. Also note that you need to make sure to override the function in the right place. For example, getEventTimeText was in $.fullCalendar.Grid, while formatRange is in $.fullCalendar.View.
Hopefully this helps other people who end up on this question.
After my previous question, I come up to the following working code that is intended to refresh the DOM periodically by replacing the <div id="test_css_id">...</div> itself. The behavior of both AJAX requests present in the below code is to reload the same code itself.
<div id="test_css_id">
<a id="link_css_id" href="test_url.html">LINK</a>
<script type="text/javascript">
var refreshTimer;
$('#link_css_id').click(function(event) {
event.preventDefault();
$.ajax({
url: $(this).attr('href'),
type: 'PUT',
success: function(data) {
clearInterval(refreshTimer);
$('#test_css_id').replaceWith(data); // Replaces all code including JavaScript with the response data (note: the response data is exactly the same as the code shown here).
}
});
});
$(document).ready(function() {
function refreshFunction(){
$.ajax({
url: 'test_url.html',
type: 'GET',
success: function(data) {
clearInterval(refreshTimer);
$('#test_css_id').replaceWith(data); // Replaces all code including JavaScript with the response data (note: the response data is exactly the same as the code shown here).
}
});
}
refreshTimer = setInterval(refreshFunction, 1000);
});
</script>
</div>
However, as said by the author of the accepted answer, "there are other ways you can do it [..] one way is to wrap all of that code into a module". I am not expert in JavaScript but I would like to understand and learn it a little more.
How can I wrap all of that code into a module in order to avoid using global variables?
Your current code looks like this:
var refreshTimer; //a global variable
$(...).click(...);
To make refreshTimer not global, you need to put it inside a function:
function main(){
var refresherTimer; //this variable is now local to "main"
$(...).click(...);
}
main();
However, doing it this way won't solve the problem completely. While we did get rid of the global variables, we added a new one - the "main" function itself.
The final trick is to turn the "main" function into an anonymous function and invoke it directly. This is the famous "module pattern":
(function(){
var refreshTimer; //local variable to that anonymous function
$(...).click(...);
}()); //and here we call the function to run the code inside it.
The extra parenthesis around everything are important. If you do just function(){}() instead of (function(){}()) then you will get a syntax error.
Here's a nice description of the module pattern in JavaScript.
This is a bit of a tricky question.
I am very familiar with javascript, however I am on a project that auto-crawls a website using PhantomJS and CasperJS. These are entirely new subjects to me.
I was able to figure out how to use Casper and navigate, log in to pages, etc, however it is unweildy as the general flow seems to be:
casper.start('http://google.fr/');
casper.then(function() {
this.echo("I'm in your google.");
});
casper.then(function() {
this.echo('Now, let me write something');
});
casper.then(function() {
this.echo('Oh well.');
});
casper.run();
My problem with this is that I want to do all sorts of things with the website, depending on what data is gotten with it. I can't pre-layout the sequence of navigations and not have it change. I hope this makes sense.
To solve this, I created a Javascript Navigator object with builtin functions. My general concept was:
navigator.logIn(function()
{
navigator.actionA(parameters, function()
{
if (navigator.data.a == navigator.data.b) {
navigator.actionB();
} else {
navigator.actionC();
}
});
});
And embedded in each of these functions would be casper functions.
Here is a shortened version of my actual code, and where things started getting funky:
var casper = require('casper').create({
clientScripts: [ 'jquery.min.js' ],
onError: function(self, m) {
console.log('FATAL:' + m);
self.exit();
},
});
var navigator = new _Navigator();
function _Navigator() { }
_Navigator.prototype.logIn = function(aCallback)
{
var self = this;
casper.start('https://website/login.asp', function()
{
if (1 == 1) {
this.evaluate(function() {
$("input[name=blah]").val('blahblah');
});
// ... A LOT MORE CODE
aCallback();
}
});
}
_Navigator.prototype.search = function(aDataSet, aCallback)
{
var self = this;
console.log('this works');
casper.then(function(){
console.log('this works');
});
var firstName = 'foobar';
casper.then(function(){
console.log('this works');
this.evaluate(function()
{
console.log('this no longer works!!');
$('input[id=blah]').val(firstName);
aCallback();
});
});
}
navigator.logIn(function() {
// LOG IN RUNS, AND CALLS BACK SUCCESSFULLY...
navigator.search({'dataset'}, function()
{
console.log('This never runs');
});
});
casper.run();
You'll notice that in the navigator.login function, I call casper.start(); In this, the evaluation function works fine, however then I do a callback function within that casper.start(); In my callback, I call the next function, navigator.search, which I suppose is still technically executing in the casper.start?
When I try running casper.evaluate within this new function called by the first callback function, everything seems to behave fine with the exception that casper.evaluate no longer works! It seems to eat the function, not printing any console logs or anything.
I have tried everything on this. I am not sure how to do this correctly. Does anyone have any suggestions on what I am doing wrong? Thanks.
I know this is quite old, but: What's going on here is a combination of two issues:
casper.evaluate() seems to eat all errors within the current stack - onError won't run from inside an .evaluate() callback.
Functions used in .evaluate are not standard closures - they're sandboxed, and have no access to variables outside their scope, unless passed as explicit arguments to casper.evaluate. So in the evaluated function where you call aCallback() there's no aCallback in scope, and the function will fail (silently) with a ReferenceError.
casper.evaluate() is as a window onto the headless browser session.
Anything that happens in functions passed to evaluate doesn't appear on your local console.
However, you can either log any value returned from evaluate or print all output by setting up a listener:
casper.on('remote.message', function(message) {
console.log(message);
});
I'm new in javascript and jQuery.
I'm using ajax calls to get data from my server. The fact is, I'm losing my javascript variables after the call ..
Here is what I did : the variable is define outside any function and treat in an other function.
var a = 0;
function myfunction(url){
$.ajax({
url: url,
timeout: 20000,
success: function(data){
// Do some stuff
// The a variable is now undefined
},
error: function(){
// Do some stuff
}
});
}
Everything is working fine, the only thing is that I need to keep my variables ... but it looks like it's gone ..
Does anyone know why?
Thanks
You say you're using your variable in another function (but don't show us that function). However, that function is probably running before your AJAX call is complete. This is what "asynchronous" means -- they don't take place at the same time.
To fix this, add some more code inside your success callback, where it will run only after the a variable is changed.
This works and the url does stay in scope. What you should check is if you are getting an error - this will prevent success from running (toss an alert("error"); or something similar in there to test).
I use Firebug in FireFox to help me out.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script type="text/javascript">
var a = 0;
function doSomething (url){
$.ajax({
url: url,
timeout: 20000,
success: function(data){
alert(a);
},
error: function(){
// Do some stuff
}
});
}
</script>
Do it
I've been working with getters and setters to avoid the prospect of using global variables. However, I've run into a problem. The below code, which works fine with integer variables, throws an exception when I try to run an AJAX call instead. Can someone explain to me why this is happening?
function Object_XML() {
me = this;
me.xml = null;
}
Object_XML.prototype = {
getXML: function() {
return me.xml
},
setXML: function(data) {
me.xml = data;
},
loadXML: function() {
$.ajax({
type: "GET",
url: "questions.xml",
dataType: "xml",
success: function(xml) {
me.setXML(xml);
} //close success
});//close AJAX
}//close setXML
};
$(document).ready(function() {
var data = new Object_XML();
alert("This is an " + data.getXML());
data.setXML();
alert("This is an " + data.getXML());
});
Thanks, Elliot Bonneville
You just negated your use of private variables with getters and setters by using me = this; You just made me a global variable by not using var. (any variable not defined using var gets attached to the global namespace)
In your case since you're working within the same object scope you can just use this and avoid the me as personally, i think it's confusing. But if you want to stick to that paradigm, use var me = this;
Your example is really unclear, where does the error happen? You're calling data.setXml() with no parameters, so me.xml will bet set to undefined. That is to be expected if you pass nothing into the method.
Also keep in mind that due to the async nature of your call, if you were to do something like:
data.loadXml();
console.log("data.getXML();", data.getXML()); // would be undefined
data.getXML() at that moment would still be undefined as it's likely your asynchronous call hasn't returned yet, thus not setting the xml attribute of your object.