How to call a function inside SpookyJS? - node.js

I have a function called clickMore:
function clickMore(max, i){
i = i || 0;
if ((max == null || i < max) && this.visible(moreButton)) { // synchronous
// asynchronous steps...
this.thenClick(moreButton); // sometimes the click is not properly dispatched
this.echo('click');
this.waitUntilVisible(loadingButton);
this.waitUntilVisible(moreButton, null, function onTimeout(){
// only placeholder so that the script doesn't "die" here if the end is reached
});
this.then(function(){
//this.capture("business_"+i+".png"); //captures a screenshot of the page
clickMore.call(this, max, i+1); // recursion
});
}
}
I would like to call that function from spooky here:
spooky.then(function(){
clickMore.call(spooky);
})
I've looked through the Spooky docs, and know that I'll probably need to use a function tuple, but not sure how to implement. How can I go about doing this?
UPDATE:
Tried using a function tuple from the SpookyJS documentation with no luck:
spooky.then([{
clickMore: clickMore
}, function(){
clickMore.call(spooky);
}]);

Functions passed into spooky.then will execute in the Casper/PhantomJS JavaScript environment. They do not have access to Spooky's Node.js JS environment.
spooky.then([{
clickMore: clickMore
}, function(){
clickMore.call(spooky); // <- spooky is not defined here
}]);
Please have another look at what the Introduction wiki page says about JavaScript Environments and the fact that they are isolated.

Related

Why is the middleware named in the below example [duplicate]

I have a major problem with profiling in javascript with anonymous functions, I have always many anonymous functions - most of them are callbacks - and It makes analyzing results of profiler very hard for me.
Finally I decided to use named functions for callbacks, like this:
var f = function(callback) {
// Do something ...
callback();
}
f(function named_function() {
console.log('Sample callback function!');
});
I want to know that will I have any problems after making this change in my codes?
And will this type of function definition and passing reserve the name (named_function) anywhere?
The name will only be available inside the scope of the named function expression.
But there is a problem in IE 8 and lower. It will leak out to the outer scope, and will actually create a different function object, so you should nullify it if that's a problem.
f(function named_function() {
console.log('Sample callback function!');
});
var named_function = null;
See this article for more information: Named function expressions demystified
Or you could create it like this to solve the IE issue.
f(function() {
return function named_function() {
console.log('Sample callback function!');
};
}());
But that's a little ugly.
If you pass anonymous functions like that, the name will exist inside the function itself.
It will not exist in any other scope.
var f = function(callback) {
// Do something ...
callback();
}
f(function named_function() {
console.log(named_function); // Logs the function
console.log('Sample callback function!');
});
console.log(named_function);​ // Error: named_function is undefined
You don't have to complicate things.
Just name the function when you declare it
var foo = function foo(){};
Defining a named callback in a scope will cause it to be visible only in that scope. I would therefore conclude that there shouldn't be any naming conflicts. For example, the following code runs as expected:
(function named_callback() { console.log("Callback 1"); })();
(function named_callback() { console.log("Callback 2"); })();​
Actually, you're still creating an anonymous function expression and assigning it to a local scoped variable f. Go for
function f( callback ) {
callback();
}
f( named_function );
function named_function() {
console.log('Sample callback function!');
}
That way, you're even avoiding the named function expression memory leak in <= IE8, plus since you're no longer creating a function expression but a *function declaration, you can even access f within the body.

Trying to understand node.js callback when the function normally doesn't expect any parameters?

I am trying to work with node.js and node-java and trying to get my head wrapped around some concepts, and in particular how to write async method calls.
I think that, for a function in Java, myclass.x():
[In Java]:
Z = myclass.x(abc);
And:
[In node.js/node-java]:
myclass.x(abc, function(err,data) {
//TODO
Z = data;});
In other words, the myclass.x function gets evaluated using the parameter abc, and if no error, then the result goes into "data" which is then assigned to Z.
Is that correct?
Here's the thing (or one of the things) that I am confused about.
What happens if the function myclass.x() doesn't take any parameters?
In other words, it is normally (in Java) just called like:
Z = myclass.x();
If that is the case, how should the node.js code look?
myclass.x(, function(err,data) {
//TODO
Z = data;});
doesn't seem right, but:
myclass.x( function(err,data) {
//TODO
Z = data;});
also doesn't seem correct.
So what is the correct way to code the node.js code in this case?
Thanks in advance!!
Jim
EDIT 1: Per comments, I'm adding the specific code I'm working with is the last couple of commented out lines from this other question at:
node.js and node-java: What is equivalent node.js code for this java code?
These are the lines (commented out in that other question):
var MyFactoryImplClass = java.import("oracle.security.jps.openaz.pep.PepRequestFactoryImpl.PepRequestFactoryImpl");
var result = myFactoryImplClass.newPepRequest(newSubject, requestACTIONString ,requestRESOURCEString , envBuilt)
I tried to make the last line use an async call:
MyFactoryImplClass.getPepRequestFactory( function(err,data) {
//TODO
pepReqF1=data;})
javaLangSystem.out.printlnSync("Finished doing MyFactoryImplClass.getPepRequestFactory() and stored it in pepReqF1 =[" + pepReqF1 + "]");
But the output was showing the value of that pepReqF1 as "undefined".
If calling the method with one parameter and a callback is:
myclass.x(abc, function(err, data) {
// ...
});
Then calling a method with only a callback would be:
myclass.x(function(err, data) {
// ...
});
The function(err, data) { } part is just a normal parameter just like abc. In fact, you can pass a named function with:
function namedFun(err, data) {
// ...
}
myclass.x(abc, namedFun);
Or even:
var namedFun = function (err, data) {
// ...
}
myclass.x(abc, namedFun);
Functions in JavaScript are first-class objects like strings or arrays. You can pass a named function as a parameter to some other function:
function fun1(f) {
return f(10);
}
function fun2(x) {
return x*x;
}
fun1(fun2);
just like you can pass a named array:
function fun3(a) {
return a[0]
}
var array = [1, 2, 3];
fun3(array);
And you can pass an anonymous function as a parameter:
function fun1(f) {
return f(10);
}
fun1(function (x) {
return x*x;
});
just like you can pass an anonymous array:
function fun3(a) {
return a[0]
}
fun3([1, 2, 3]);
There is also a nice shortcut so that instead of:
fun1(function (x) {
return x*x;
});
You can write:
fun1(x => x*x);
Making my comment into an answer...
If the issue you're experiencing is that Z does not have the value you want when you are examining it, then that is probably because of a timing issue. Asynchronous callbacks happen at some unknown time in the future while the rest of your code continues to run. Because of that, the only place you can reliably use the result passed to the asynchronous callback is inside the callback itself or in some function you would call from that function and pass it the value.
So, if your .x() method calls it's callback asynchronously, then:
var Z;
myclass.x( function(err,data) {
// use the err and data arguments here inside the callback
Z = data;
});
console.log(Z); // outputs undefined
// you can't access Z here. Even when assigned
// to higher scoped variables because the callback has not yet
// been called when this code executes
You can see this is a little more clearly by understanding the sequencing
console.log('A');
someAsyncFucntion(function() {
console.log('B');
})
console.log('C');
This will produce a log of:
A
C
B
Showing you that the async callback happens some time in the future, after the rest of your sequential code has executed.
Java, on the other hand, primarily uses blocking I/O (the function doesn't return until the I/O operation is copmlete) so you don't usually have this asynchronous behavior that is standard practice in node.js. Note: I believe there are some asynchronous capabilities in Java, but that isn't the typical way things are done and in node.js, it is the typical ways things are done.
This creates a bit of an architectural mismatch if you're trying to port code that uses I/O from environment from another because the structure has to be redone in order to work properly in a node.js environment.

this.async() in yeoman-generator

I am learning how to write a yeoman-generator. I have a question regarding the code below. It says by adding var done = this.async(); and call the method later in callback, we can make the function askFor() a asynchronized function. Could someone please explain why?
askFor: function() {
var done = this.async();
// Have Yeoman greet the user.
this.log(yosay('Welcome to the marvelous Myblog generator!'));
var prompts = [{
name: 'blogName',
message: 'What do you want to call your blog?',
default: 'myblog'
}];
this.prompt(prompts, function(props) {
this.blogName = props.blogName;
done();
}.bind(this));
}
Here is the code of this.async
this.async = function() {
return function() {};
}
Just fell on this question by pure coincidence searching for something else.
Actually, this.async is overwritten on each method during the run phase to either delay execution until completion or run synchronously.
You can read the relevant code line here:
https://github.com/yeoman/generator/blob/master/lib/base.js#L372-L393
So basically, behind the scenes Yeoman always call a callback. When you call this.async() we keep a reference variable and return the callback. If you don't call it, we take care of calling the callback manually after the function end.

What's going on with Meteor and Fibers/bindEnvironment()?

I am having difficulty using Fibers/Meteor.bindEnvironment(). I tried to have code updating and inserting to a collection if the collection starts empty. This is all supposed to be running server-side on startup.
function insertRecords() {
console.log("inserting...");
var client = Knox.createClient({
key: apikey,
secret: secret,
bucket: 'profile-testing'
});
console.log("created client");
client.list({ prefix: 'projects' }, function(err, data) {
if (err) {
console.log("Error in insertRecords");
}
for (var i = 0; i < data.Contents.length; i++) {
console.log(data.Contents[i].Key);
if (data.Contents[i].Key.split('/').pop() == "") {
Projects.insert({ name: data.Contents[i].Key, contents: [] });
} else if (data.Contents[i].Key.split('.').pop() == "jpg") {
Projects.update( { name: data.Contents[i].Key.substr(0,
data.Contents[i].Key.lastIndexOf('.')) },
{ $push: {contents: data.Contents[i].Key}} );
} else {
console.log(data.Contents[i].Key.split('.').pop());
}
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
if (Projects.find().count() === 0) {
boundInsert = Meteor.bindEnvironment(insertRecords, function(err) {
if (err) {
console.log("error binding?");
console.log(err);
}
});
boundInsert();
}
});
}
My first time writing this, I got errors that I needed to wrap my callbacks in a Fiber() block, then on discussion on IRC someone recommending trying Meteor.bindEnvironment() instead, since that should be putting it in a Fiber. That didn't work (the only output I saw was inserting..., meaning that bindEnvironment() didn't throw an error, but it also doesn't run any of the code inside of the block). Then I got to this. My error now is: Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.
I am new to Node and don't completely understand the concept of Fibers. My understanding is that they're analogous to threads in C/C++/every language with threading, but I don't understand what the implications extending to my server-side code are/why my code is throwing an error when trying to insert to a collection. Can anyone explain this to me?
Thank you.
You're using bindEnvironment slightly incorrectly. Because where its being used is already in a fiber and the callback that comes off the Knox client isn't in a fiber anymore.
There are two use cases of bindEnvironment (that i can think of, there could be more!):
You have a global variable that has to be altered but you don't want it to affect other user's sessions
You are managing a callback using a third party api/npm module (which looks to be the case)
Meteor.bindEnvironment creates a new Fiber and copies the current Fiber's variables and environment to the new Fiber. The point you need this is when you use your nom module's method callback.
Luckily there is an alternative that takes care of the callback waiting for you and binds the callback in a fiber called Meteor.wrapAsync.
So you could do this:
Your startup function already has a fiber and no callback so you don't need bindEnvironment here.
Meteor.startup(function () {
if (Projects.find().count() === 0) {
insertRecords();
}
});
And your insert records function (using wrapAsync) so you don't need a callback
function insertRecords() {
console.log("inserting...");
var client = Knox.createClient({
key: apikey,
secret: secret,
bucket: 'profile-testing'
});
client.listSync = Meteor.wrapAsync(client.list.bind(client));
console.log("created client");
try {
var data = client.listSync({ prefix: 'projects' });
}
catch(e) {
console.log(e);
}
if(!data) return;
for (var i = 1; i < data.Contents.length; i++) {
console.log(data.Contents[i].Key);
if (data.Contents[i].Key.split('/').pop() == "") {
Projects.insert({ name: data.Contents[i].Key, contents: [] });
} else if (data.Contents[i].Key.split('.').pop() == "jpg") {
Projects.update( { name: data.Contents[i].Key.substr(0,
data.Contents[i].Key.lastIndexOf('.')) },
{ $push: {contents: data.Contents[i].Key}} );
} else {
console.log(data.Contents[i].Key.split('.').pop());
}
}
});
A couple of things to keep in mind. Fibers aren't like threads. There is only a single thread in NodeJS.
Fibers are more like events that can run at the same time but without blocking each other if there is a waiting type scenario (e.g downloading a file from the internet).
So you can have synchronous code and not block the other user's events. They take turns to run but still run in a single thread. So this is how Meteor has synchronous code on the server side, that can wait for stuff, yet other user's won't be blocked by this and can do stuff because their code runs in a different fiber.
Chris Mather has a couple of good articles on this on http://eventedmind.com
What does Meteor.wrapAsync do?
Meteor.wrapAsync takes in the method you give it as the first parameter and runs it in the current fiber.
It also attaches a callback to it (it assumes the method takes a last param that has a callback where the first param is an error and the second the result such as function(err,result).
The callback is bound with Meteor.bindEnvironment and blocks the current Fiber until the callback is fired. As soon as the callback fires it returns the result or throws the err.
So it's very handy for converting asynchronous code into synchronous code since you can use the result of the method on the next line instead of using a callback and nesting deeper functions. It also takes care of the bindEnvironment for you so you don't have to worry about losing your fiber's scope.
Update Meteor._wrapAsync is now Meteor.wrapAsync and documented.

How do I call a basic YUI3 function from within a normal JavaScript function?

I'd like to call a simple YUI3 function from within a JavaScript function.
Here is some code that does what I want in a very verbose way:
function changeContent (message) {
YUI().use("node", function(Y) {
Y.all('#content-div').setContent(message);
});
}
Is there a better way to do this?
NOTE: I don't want to attach this function to any event, I just want a global changeContent() function available.
If you want the API to exist outside of the YUI().use(...function (Y) { /* sandbox */ }), you can capture the returned instance from YUI().
(function () { // to prevent extra global, we wrap in a function
var Y = YUI().use('node');
function changeContent(message) {
Y.one('#content-div').setContent(message);
}
...
})();
Be aware that there is a race condition here if you use the seed file (yui-min.js) and dynamic loader to pull in the other modules. changeContent could be called before the Node API is loaded and added to Y. You can avoid this by using a combo script up front. You can get the combo script url from the YUI 3 Configurator. There is a performance penalty for loading the modules in a blocking manner up front. You may or may not notice this in your application.
You can do like this:
(function(){
YUI().use("node", function(Y) {
APP = {
changeContent: function(message){
Y.all('.content-div').setContent(message);
}
};
});
})();
Then, you can call changeContent by calling APP.changeContent(message); from anywhere you want. Hope this helps. :D

Resources