Synchronous GM_xmlhttpRequest()s?

374 views
Skip to first unread message

ScroogeMcPump

unread,
Jun 29, 2008, 12:14:31 AM6/29/08
to greasemonkey-users
Okay, let's say have a list of URLs I want to look at from within a
Greasemonkey script. They're probably not on the same domain as the
current page, so I have to use GM_xmlhttpRequest instead of
XMLHttpRequest; BUT, there's no need to fetch list[3] if I find
everything I need at list[2], so the fetches have to be synchronous -
which GM_xmlhttpRequest isn't.

Is there a decent way around this?

Joachim Ott

unread,
Jun 29, 2008, 7:31:41 AM6/29/08
to greasemonkey-users
You already described the algorithm, all that's left is to code it.

var list = // collect links here
var listindex = 0;

var http_callback = function (responsetext) {
if (responstext) { ...}
if (list[listindex]) // make_next_request
}

http_callback("");

ScroogeMcPump

unread,
Jun 29, 2008, 11:39:05 AM6/29/08
to greasemonkey-users
On Jun 29, 7:31 am, Joachim Ott <Joachim....@gmail.com> wrote:

> var list = // collect links here
> var listindex = 0;
>
> var http_callback = function (responsetext) {
> if (responstext) { ...}
> if (list[listindex]) // make_next_request
>
> }
>
> http_callback("");

I'm sorry, but it seems the sections of code you glossed over are
precisely the ones I'm having trouble grasping. I guess I'm not as
clear on callbacks and recursive code as I should be, so bear with me
while I go thru this.

You define the list, a counter for the list, and the callback function
for GM_xmlhttpRequest to use (presumably "onload", but probably
"onerror" as well), but then, instead of doing an XHR directly, you
call the callback function yourself, with an empty response text
(actually, the response variable for an XHR callback is a five-element
object, not just text, so maybe this should be [] or null instead),
and you let the callback kick off an XHR for the "next" (first) item
in the list.

However (and here's where it gets murky for me), the XHR call returns
immediately (before the fetch is actually finished), and the callback
proceeds to end of function, whereupon IT returns to the line after
the callback call - without knowing if there's any data to process yet
or not.

Yes, when XHR recursively calls the callback that called it (say that
five times fast!), it'll test things and decide whether to go with
what it got or make another XHR, but by that time, the main line code
has ALREADY proceeded without it.

Where's the part that gets the main line code to STOP until either the
response I need is received, or the list of URLs is exhausted?

Anthony Lieuallen

unread,
Jun 29, 2008, 1:31:00 PM6/29/08
to greasemon...@googlegroups.com
On 6/29/2008 11:39 AM, ScroogeMcPump wrote:
> I'm sorry, but it seems the sections of code you glossed over are
> precisely the ones I'm having trouble grasping.


Full example:


// ==UserScript==
// @name Synchronous XHR
// ==/UserScript==

var urls=[
'http://www.google.com/',
'http://www.reddit.com/',
'http://www.digg.com/'
];
var urlIndex=0;

function processor(xhr) {
if (xhr) {
GM_log('fetched '+xhr.finalUrl);

if (xhr.responseText.match(/reddit/i)) {
// stop processing on this condition
return;
}
}

if (urls[urlIndex]) {
GM_xmlhttpRequest({
'method':'GET',
'url':urls[urlIndex],
'onload':processor
});
urlIndex++;
}
}

processor();

ScroogeMcPump

unread,
Jun 29, 2008, 8:04:10 PM6/29/08
to greasemonkey-users
I'm still not getting it.

I pasted your code into a .user.js, made one modification (adding
"GM_log('done');" to the end), installed and ran it, and got this from
the Error Console:

/Synchronous XHR: done
/Synchronous XHR: fetched http://www.google.com/
/Synchronous XHR: fetched http://www.reddit.com/

- in that order. That's still telling me that the code to do what I
want with the proper response is going to get executed before that
response (or any response) is obtained.

Joachim Ott

unread,
Jun 30, 2008, 6:04:42 AM6/30/08
to greasemonkey-users
On 30 Jun., 02:04, ScroogeMcPump <gasbuddyphi...@yahoo.com> wrote:
> I'm still not getting it.
>
> I pasted your code into a .user.js, made one modification (adding
> "GM_log('done');" to the end), installed and ran it, and got this from
> the Error Console:
>
> /Synchronous XHR: done
> /Synchronous XHR: fetchedhttp://www.google.com/
> /Synchronous XHR: fetchedhttp://www.reddit.com/
>
> - in that order.  That's still telling me that the code to do what I
> want with the proper response is going to get executed before that
> response (or any response) is obtained.

The "GM_log('done');" doesn't belong to the end. Make it look like
this:

// ==UserScript==
// @name Synchronous XHR
// ==/UserScript==

var urls=[
'http://www.google.com/',
'http://www.reddit.com/',
'http://www.digg.com/'
];
var urlIndex=0;

function processor(xhr) {
if (xhr) {
GM_log('fetched '+xhr.finalUrl);

if (xhr.responseText.match(/reddit/i)) {
// stop processing on this condition
GM_log('Done');
return;
}
}

if (urls[urlIndex]) {
GM_xmlhttpRequest({
'method':'GET',
'url':urls[urlIndex],
'onload':processor
});
urlIndex++;
}
else
GM_log('all URLs processed');
}

processor();
GM_log('processing started');

ScroogeMcPump

unread,
Jul 1, 2008, 11:10:42 AM7/1/08
to greasemonkey-users
On Jun 30, 6:04 am, Joachim Ott <Joachim....@gmail.com> wrote:

> The "GM_log('done');" doesn't belong to the end.

So you're saying that ALL the code for after the XHR(s) has to go
WITHIN processor()?

Very counter-intuitive - especially for an old-school coder like me.

ScroogeMcPump

unread,
Jul 8, 2008, 8:22:37 PM7/8/08
to greasemonkey-users
Next question:

> if (xhr.responseText.match(/reddit/i)) {
> // stop processing on this condition
> GM_log('Done');
> return;
> }

My test to end the recursion is more complex than that, and depends on
the values of variables set outside of the processor() function. How
can I pass these variables into processor() (and thru all of its
multiple invocations), when processor()'s parameter format is
predefined by the fact that it's used as the callback for the XHR?
Reply all
Reply to author
Forward
0 new messages