I'm trying to get back a value from a script executed from inside a chrome application but if the value is coming from a function in the script, I can't get it back.
Here is an example that explains my problem.
First something that works...
In popup.js
chrome.tabs.executeScript(tabId,
{
file: 'test.js',
}, function(results)
{
console.log(results)
});
in test.js (simplified code)
document.getElementById("anything").innerHTML
In this case, the console.log returns the expected value
Now where I'm struggling...
I keep the same popup.js code but I change test.js with:
function myfunction ()
{
do some stuff here
}
myfunction().then(function(results)
{
console.log(results);
results;
});
In that case the console.log returns the value I'm expecting but how can I make it accessible for popup.js ? I need to wait until myfunction is finally executed to make sure I get the right data but that prevents me from simply outputting the content like in my first example. There must be something simple but I'm no expert.
Thanks
Laurent
Related
I am trying to write a controller in sailsjs
module.exports = {
someFunction: function(req, res){
var userID = req.param('id')
User.findUserName(userID, function(err, receiverName){
if(err){
res.serverError(err);
}else{
ReceiverName = receiverName;
}
});
console.log("#####################################");
console.log(ReceiverName);
console.log("#####################################");
.
.
.
.
But when I deploy the controller, restart the sails app and call this function through the URL, I am getting the following error on the console:
error: Sending 500 ("Server Error") response:
ReferenceError: ReceiverName is not defined
I tried to declare the variable ReceiverName to be global, but still cant sort it out. I need guidance on making it global so that I assign value to it inside the else part and use it anywhere inside the controller.
Note 1:
However, when I am printing the value of the variable on console right inside the else block using the code:
User.findUserName(userID, function(err, receiverName){
if(err){
res.serverError(err);
}else{
console.log("#####################################");
console.log(receiverName);
console.log("#####################################");
}
});
the value is printed perfectly.
Note 2:
I am already making asynchronous call to retrieve the receiverName, so it is not a doubt about returning the response from asynchronous calls. I am looking for method to store the response in a variable that can be used further.
Your Note 2 is a little bit confusing so I'm not sure if you understand that the reason it does not work in your first example is because the User.findUserName() has not finished running before you execute your console.log().
Remember, the database calls are async. So any values you want to use from that database call have to run after it finishes. The only way to ensure that is to make sure your code runs inside the callback.
That is why the second example works. The code that needed the receiverName variable ran inside the callback. Try this, it is pretty much the same thing as your second example ...
User.findUserName(userID, function(err, receiverName){
if(err){
res.serverError(err);
}else{
doStuffWith(receiverName)
}
});
var dostuffWith = function(ReceiverName){
// all the code that you want to use ReceiverName should be in here.
console.log("#####################################");
console.log(ReceiverName);
console.log("#####################################");
}
You should do some reading on programming patterns with Node.js, async and callbacks. Like this
http://book.mixu.net/node/ch7.html
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.
I've got a NodeJS app i'm building (using Sails, but i guess that's irrelevant).
In my action, i have a number of requests to other services, datasources etc that i need to load up. However, because of the huge dependency on callbacks, my code is still executing long after the action has returned the HTML.
I must be missing something silly (or not quite getting the whole async thing) but how on earth do i stop my action from finishing until i have all my data ready to render the view?!
Cheers
I'd recommend getting very intimate with the async library
The docs are pretty good with that link above, but it basically boils down to a bunch of very handy calls like:
async.parallel([
function(){ ... },
function(){ ... }
], callback);
async.series([
function(){ ... },
function(){ ... }
]);
Node is inherently async, you need to learn to love it.
It's hard to tell exactly what the problem is but here is a guess. Assuming you have only one external call your code should look like this:
exports.myController = function(req, res) {
longExternalCallOne(someparams, function(result) {
// you must render your view inside the callback
res.render('someview', {data: result});
});
// do not render here as you don't have the result yet.
}
If you have more than two external calls your code will looks like this:
exports.myController = function(req, res) {
longExternalCallOne(someparams, function(result1) {
longExternalCallTwo(someparams, function(result2) {
// you must render your view inside the most inner callback
data = {some combination of result1 and result2};
res.render('someview', {data: data });
});
// do not render here since you don't have result2 yet
});
// do not render here either as you don't have neither result1 nor result2 yet.
}
As you can see, once you have more than one long running async call things start to get tricky. The code above is just for illustration purposes. If your second callback depends on the first one then you need something like it, but if longExternalCallOne and longExternalTwo are independent of each other you should be using a library like async to help parallelize the requests https://github.com/caolan/async
You cannot stop your code. All you can do is check in all callbacks if everything is completed. If yes, go on with your code. If no, wait for the next callback and check again.
You should not stop your code, but rather render your view in your other resources callback, so you wait for your resource to be reached before rendering. That's the common pattern in node.js.
If you have to wait for several callbacks to be called, you can check manually each time one is called if the others have been called too (with simple bool for example), and call your render function if yes. Or you can use async or other cool libraries which will make the task easier. Promises (with the bluebird library) could be an option too.
I am guessing here, since there is no code example, but you might be running into something like this:
// let's say you have a function, you pass it an argument and callback
function myFunction(arg, callback) {
// now you do something asynchronous with the argument
doSomethingAsyncWithArg(arg, function() {
// now you've got your arg formatted or whatever, render result
res.render('someView', {arg: arg});
// now do the callback
callback();
// but you also have stuff here!
doSomethingElse();
});
});
So, after you render, your code keeps running. How to prevent it? return from there.
return callback();
Now your inner function will stop processing after it calls callback.
Using Node.js and the node-postgres module to communicate with a database, I'm attempting to write a function that accepts an array of queries and callbacks and executes them all asynchronously using the same database connection. The function accepts a two-dimensional array and calling it looks like this:
perform_queries_async([
['SELECT COUNT(id) as count FROM ideas', function(result) {
console.log("FUNCTION 1");
}],
["INSERT INTO ideas (name) VALUES ('test')", function(result) {
console.log("FUNCTION 2");
}]
]);
And the function iterates over the array, creating a query for each sub-array, like so:
function perform_queries_async(queries) {
var client = new pg.Client(process.env.DATABASE_URL);
for(var i=0; i<queries.length; i++) {
var q = queries[i];
client.query(q[0], function(err, result) {
if(err) {
console.log(err);
} else {
q[1](result);
}
});
}
client.on('drain', function() {
console.log("drained");
client.end();
});
client.connect();
}
When I ran the above code, I expected to see output like this:
FUNCTION 1
FUNCTION 2
drained
However, the output bizarrely appears like so:
FUNCTION 2
drained
FUNCTION 2
Not only is the second function getting called for both requests, it also seems as though the drain code is getting called before the client's queue of queries is finished running...yet the second query still runs perfectly fine even though the client.end() code ostensibly killed the client once the event is called.
I've been tearing my hair out about this for hours. I tried hardcoding in my sample array (thus removing the for loop), and my code worked as expected, which leads me to believe that there is some problem with my loop that I'm not seeing.
Any ideas on why this might be happening would be greatly appreciated.
The simplest way to properly capture the value of the q variable in a closure in modern JavaScript is to use forEach:
queries.forEach(function(q) {
client.query(q[0], function(err, result) {
if(err) {
console.log(err);
} else {
q[1](result);
}
});
});
If you don't capture the value, your code reflects the last value that q had, as the callback function executed later, in the context of the containing function.
forEach, by using a callback function isolates and captures the value of q so it can be properly evaluated by the inner callback.
A victim of the famous Javascript closure/loop gotcha. See my (and other) answers here:
I am trying to open 10 websocket connections with nodejs, but somehow my loop doesnt work
Basically, at the time your callback is executed, q is set to the last element of the input array. The way around it is to dynamically generate the closure.
It will be good to execute this using async module . It will help you to reuse the code also . and will make the code more readable . I just love the auto function provided by async module
Ref: https://github.com/caolan/async
I got the following code (in a seperate file called page.js):
var page = new function() {
this.getImdbID = function(){
var imdbid = '';
chrome.tabs.getSelected(null, function(tab) {
imdbid='0944835';
});
return imdbid;
};
}
Which gets called by the following code (which is in background.html).
var imdbid = page.getImdbID();
This code only works when I place a breakpoint on the "return imdbid;" row. When I skip the breakpoint it only returns an empty string. Is there anything I have missed?
The fact that it works when you place a breakpoint suggests a timing issue.
In this case, I would suppose that getSelected is an asynchronous operation (hence why it takes a callback), and so you would need to wait for it to complete and the callback to be executed before the variable has the value you want.