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
Related
I am calling a function that loads JSON from a server and displays it in a div, that gets dynamically created in the DOM. After the created div is displayed, I want to do other stuff like parent.appendChild(x). Now it sometimes happens that the second step gets run before the first. I've tried to use this code, but it doesn't seem to work.
$.ajax({
url: firstfunction(),
success: function() {
parent.appendChild(x)
}
});
Note that firstfunction() loads JSON and displays it inside a newly created div.
Any ideas how to fix this problem?
You should use promises, so you wait to finish your Ajax request before moving on to another task (for your example manipulate DOM).
async function doAjax(ajaxurl){
const result = await $.ajax({
url: ajaxurl
});
return result;
}
//Call function
doAjax().then( data => doStuff(data) )
Async/Await with $.ajax example:
https://codepen.io/anon/pen/xpzJza
I fixed the problem by just using a synchronous httprequest.
Will it be a good or a bad practice to override all ajax call in my Web App's JavaScript code with an "invisible" Cache layer?
It'd be something like (pseudo-code):
$.prototype.ajax = function(...) {
if( requested URL can be found in localStorage )
return dataFromLocalStorage; // as Deferred
data = invoke original $.ajax;
localStorage.setItem(key, data);
return data; // as Deferred
}
I can think of one pro: no refactoring is needed for existing ajax calls, and one con: future developers will be unaware of this functionality, as it disguises itself as a regular ajax call.
What do you think? Will it be a good practice or should I avoid it?
No, it is not a good idea to override the default behavior of $.ajax like this. Other code on your page including jQuery plugins might depend on the default behavior.
It is likely at some point you might want to get the freshest version of some data, if $.ajax always caches to localStorage there will be no way to do that. This could also lead to future debugging headaches when someone working on the code (including you) can't figure out why their AJAX calls keep returning stale data.
It would be much better to just implement the caching in a separate function. That way when you see a call to something like ajaxWithCaching it will be obvious that something more that a plain AJAX call is going on.
I wouldn't be overriding the default behavior of anything! If you change the way $.ajax works, what will you do when you're using a lightbox plugin (or literally anything) which relies on the normal functionality of $.ajax?
It would backfire terribly upon you.
Instead, create your own function which performs the caching, and uses $.ajax normally.
var cache={}; // Your Cache
function getArticle(id,callback){ // Your caching ajax-wrapping function (requires a callback)
// Require the arguments
if (arguments.length<2) throw "getArticle requires id and callback arguments";
// If the article is cached, pass it to the callback and return
var cachedArticle = id in cache
? cache[id]
: null;
if (cachedArticle) return callback(cachedArticle,true); // cached=true
// If that article isn't in the cache, perform an ajax call to get it
$.ajax({
url: "article.php",
success: function(data,textStatus,jqXHR){
cache[id] = data; // Cache it!
callback(data,false); // Pass it to the callback // cached=false
}
});
return true; // reporting that we launched an ajax request
}
Then to use it, you've got a pretty robust little function.
var ajaxWasUsed = getArticle(8, function(articleData,wasCached){
console.log("Got Article #8 "+(wasCached?"(from cache)":"(fresh ajax)")+":",articleData);
});
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);
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.
in jQuery, I iterate over an xml list of areas and do a POST request to get detailed information about each area. Because sending thousands of requests at once is debilitating for the client and server, I would like to set a flag so that I wait for a request to finish before sending the subsequent [next] request.
if the xml looks like this:
<area>5717</area>
<area>5287</area>
<area>5376</area>
then the xml parsing kinda looks like:
$(xml).find("area").each( function() {
doPost();
}
and the doPost() function looks like
doPost : function () {
$.post( ... )
}
Basically, I would like to add a toggling "wait" but I'm not sure how to achieve this. Is there a way I can keep the essential ".each" iteration or is another type of loop better for this?
Thanks in advance.
A general algorithm off the top of my head:
You could put the whole list into an array. Take the first item of the array and post it. In the success handler of your post you could recursively call the function with the next index int the list.
I wouldn't use async: false because it would then be a blocking operation, which I assume the OP doesn't want.
You can use:
$.ajaxSetup({async:false});
at the top of your script to make your AJAX calls synchronous.
Alternately, you can replace $.post() with $.ajax() and set the async flag to false.
can you do a setTimeout ? that will allow for the function to still process asynchronous and allow for you to wait for some time in there too.
http://www.w3schools.com/js/js_timing.asp
setTimeout(function() {}, 5000)
You can refactor your doPost() function to take the <area> element to process as an argument, and chain into the next element from your success callback. Something like:
(function doPost($area) {
if ($area.length > 0) {
$.post({
// your options,
success: function() {
// your success handling...
doPost($area.next("area"));
}
});
}
})($(xml).find("area").first());
EDIT: Maybe the code above was a little too compact indeed.
Basically, the aim is to refactor your function so that it takes a jQuery object containing the next <area> element to process, or nothing if processing should stop:
function doPost($area) {
if ($area.length > 0) {
// Perform POST request and call ourselves from success callback
// with next <area> element (or nothing if there's no such element).
}
}
Then call this function with the first <area> element to process:
doPost($(xml).find("area").first());
The first code fragment in my answer does both at the same time. Functions are first-class objects in Javascript, and you can call a function you've just defined by enclosing its definition with parenthesis and providing the usual argument list, also surrounded by parenthesis.