I feel stupid because I've been trying to access this response variable for a while and I guess I do not understand closures or scope well enough, so please help.
I'm working on a chrome extension and I am sending a message from contentscript.js to background.js and receiving the response. Now I want to return the response and be able to use it in contentscript.js. Seems like something you should be able to do...
function getWords(){
var words = [];
chrome.runtime.sendMessage({detail: "words"}, function(response) {
console.log(response) // prints ["word1, "word2" ..]
words = response;
});
return words; // = []
}
UPDATE:
Thanks, I understand what my issue is now, but still would like some advice to solve it.
My question is what is the best way to "ask" the background page for a list of words if I need it immediately as a parameter in another function. Can I wait for the information to come back? Should I simply call that other function from the callback? or is there some other method?
Ideally I would like to actually implement a getWords() that doesn't return until the list comes back... Impossible? I'm open to open source libraries as well.
Because sendMessage is an asynchronous call and you are treating it as a synchronous one. You are trying to read words before the call is actually made. There is no way to wait for it. You need to use callbacks.
function getWords( callback ){
var words = [];
chrome.runtime.sendMessage({detail: "words"}, function(response) {
console.log(response) // prints ["word1, "word2" ..]
callback(response);
});
}
function processWords(words){
//do your logic in here
console.log(words);
}
getWords(processWords);
Related
I'm new to Javascript and trying to figure out how to properly write this.
$.get("https://localhost:8090/", function(response) {
console.log(response) #this works as expected
$.get("https://localhost:8090"+response), function() {
console.log('response') #nothing is printed
};
})
The response from $.get("https://localhost:8090/" is /oauth2callback. On my server (flask), I have logging enabled and I can see that the function inside the route is running properly.
The server code looks something like this:
#app.route('/oauth2callback')
def oauth2callback()
if 'code' not in flask.request.args:
logging.warning('code not in args')
auth_uri = flow.step1_get_authorize_url()
logging.warning(auth_uri)
return auth_uri
I can see in the log file that auth_uri is accurate.
However, I do not see console.log('response') being printed in my console. I assume this is due to my poor Javascript skills and not writing the callback correctly?
How should I structure this?
Thanks!
You have an extra ) by mistake
$.get("https://localhost:8090"+response), function() {
// ....................................^ remove this paren
}); // <--- and add it here
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 apologise if this is a repeated question. I have done my research but I am still unable to come up with an answer:
I am writing a small JSONP API. The desired outcome:
The programmer creates an instance of the JSON class.
var json = new jsonpRequest (
"http://url.to.web_service",
searchParam //passed in as an associative array
);
The programmer then declares var myJSON = jsonpRequest.getJsonP()
This will return the JSONP into the myJSON variable to be processed.
The API successfully takes in the parameters when the instance of the class is constructed, generates a link to the services w/ the desired parameters and injects it into the DOM via script tags using this function:
jsonpRequest.prototype.fetchJsonp = function(){
var request = that.getRequest();
//creates a script element
var jsonpCall = document.createElement('script');
//gets jsonp request
jsonpCall.src = request;
//adds script to DOM, which goes on to request the JSONP from the service.
document.head.appendChild(jsonpCall);
};
EDIT: For the sake of clarity: this definitely does return the data set that is requested.
The function I am using as my callback is:
jsonpRequest.prototype.processCallback = function(data){
alert(data);
that.setListOfResults(data);
};
It is called in the link as: &callback=jsonpRequest.prototype.processCallback
The processCallback function takes in the data from the request and stores it in a variable within the class using the setListOfResults function.
After the data has been stored I would like to return the data from the request to the myJSON variable. This is the getJsonp function that the programmer invokes in step 2 of the process:
jsonpRequest.prototype.getJsonp = function(){
that.buildRequest();
that.fetchJsonp();
return that.listOfResults;
};
The issue:
Here is what I am calling after I define my JSON API class:
var myJSON = json.getJsonp();
console.log(myJSON);
Because it is asynchronous, it is moving on to the console log before the callback function is invoked, resulting in MYJSON to be undefined. I am not sure how I can daisy chain my code together in such a way where it runs the callback before moving on.
I tried to emulate the solution from How can I get this JSONP call to return a value?. I may be missing a step or I am perhaps ignorant of something! Any help to clarify this would be greatly appreciated. Thank you in advance :).
P.S. I would like to avoid using JQuery etc... It would be beneficial to keep this strictly as raw JavaScript.
I am not sure how I can daisy chain my code together in such a way where it runs the callback before moving on.
You can’t do that. You must rely on callbacks. Add a callback parameter to your jsonpRequest constructor, and invoke that callback after you do setListOfResults(data). You may also wish to allow for setting the this context in which the callback will be executed.
jsonpRequest.prototype.processCallback = function(data){
alert(data);
that.setListOfResults(data);
that.callback.apply(that.context, [data]);
};
I've written a function which makes an asynchronous request using jQuery.
var Site = {
asyncRequest : function(url, containerId) {
$.ajax({
url : url,
onSuccess: function(data){
$(containerId).html(data);
}
});
}
}
Syntax might be slightly wrong as I'm using notepad, but hopefully you get the idea.
I call the function:
Site.asyncRequest('someurl', container1);
Site.asyncRequest('someurl', container2);
Both requests get sent and processed by the server. Two responses get sent back, which is what I expect. However, I would expect container1 and container2 to contain responses from both requests.
The problem, is that only the last response gets displayed and I can't figure out why. I don't know how the jQuery ajax keeps a track of requests/responses, so maybe this is a problem.
Say I make 5 or 10 requests, how does jQuery ajax know which response is for which request and where does it keep a track of it?
Thank you
This appears to be a Javascript scoping issue. Try the following:
var Site = {
asyncRequest: function(url, containerId) {
(function(theContainer) {
$.ajax({
url: url,
onSuccess: function(data) {
$(theContainer).html(data);
}
});
})(containerId);
}
};
This creates a separate scope for each function call, so the actual value pointed to by "theContainer" is different for each onSuccess anonymous function.
What is happening here is a single closure is getting created, due to the way that function is declared. See "A more advanced example" here: http://skilldrick.co.uk/2010/11/a-brief-introduction-to-closures/
Basically, the containerId is being shared among all instances of that onSuccess anonymous function. I haven't tested this, but I believe if you defined your asyncRequest function outside of Site, this would work.
As far as a more elegant solution to this problem, perhaps someone else will answer better.
I have a list of URLs and need to load each page, one after another.
This is my main function that i have in my Mind.
mainFunction() {
loop { // Loop through URL list
oPage = func1(URL); //Get page contents
aResult = func2(oPage); //Analyse the contents
func3(aResult); //Do current page modifications
}
}
func1 uses GM_xmlhttprequest, which is asynchronous, so oPage results in 'underfined' as function ends BEFORE the contents of a page could be retrieved.
func2 also uses GM_xmlhttprequest, so even no matter if oPage was undefined, aResult will be undefined too.
Any ideas on how to make all of this work?
func1 func2 and func3 should be reusable throughout the script, each of these functions may be used independently or together in different parts of script.
Is there any reason why you need to use Greasemonkey specific functionality? Are you doing cross site requests or something that specifically requires it? Looking at the Wiki for Greasemonkey, I can't find the option to set asynchronous to false.
Your easiest option is to include JQuery with your Greasemonkey script and use JQuerys AJAX functionality. Ofcourse, this can be done without JQuery, however, cross browser incompatibility in this area is quite the pain to handle manually.
Using JQuery, your code would look something like this:
function func1(url) {
var result;
$.ajax({
type: "GET",
url: url,
async: false,
success: function(data){
result = data;
}
});
return result;
}
and you would declare your variable oPage like this:
var oPage = func1(url);
The rest I think you can figure out yourself, good luck.
Normally you would put the calls inside of the xmlhttprequest's response handler, such that it returns immediately, and when it does get that page it then executes the required code.
If you really need to make them happen in a specific order, you can make the return for the first call the second, etc.
var urls = [];
(function recursive(list)
{
if (list[0]) // the list is not empty
GM_xmlhttpRequest({ // that would be "func1"
"url" : list[0], // first url in the list
"onload" : function(xhr)
{
var oPage = xhr.responseText, // page contents
aResult = func2(oPage); // analyse the contents
func3(aResult); // do current page modifications
list.shift(); // remove the first link of the list
recursive(list); // go to the next url in the list
}
});
else
alert("end of list");
})(urls);
haven't tested it but you got the idea