[js] xmlrequest's binnen classes, scope, en queing

Pagina: 1
Acties:

  • killercow
  • Registratie: Maart 2000
  • Laatst online: 20-02 08:34
Ik ben al even aan het stoeien met een goede oplossing voor de volgende problemen.

Verschillende browsers hebben verschillende objects nodig om xmlrequests te doen,
Verschillende browsers hebben een verschillende aantal maximum concurrent requests.
Een request binnen een class kan wel mooi variable gebruiken als pointer naar het xmlrequet obejct, maar als deze variable niet globaal is binnen het class kan je er bij de verwerkende functie niet meer bij, en als de variabele wel globaal is, kun je maar maximaal een request tegeleik doen (of je moet de pointers gaan opslaan in ene array, maar dan moet je ze ook weer recyclen.)

Hoe lossen jullie dit op?

Ik heb voorbeeld de volgende code: (btje opgeruimd)

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// functie om scopes voor events goed te houden.
function attachEventHandler(oObject, sMethodName) {
   return function () {
      return oObject[sMethodName].apply(oObject, arguments);
   };
}

function freevolution(){
 this.scrollDirection=null;
 this.scrollby=3;
 this.scrolltime=100;
 this.xmlhttp=false;
// functie binden.
 this.gui_button_bottom=document.getElementById('knopje');
 this.gui_button_bottom.onmouseover=attachEventHandler(this, "setScrollDirection");
 // functions
 this.setScrollDirection=setScrollDirection;
 this.fetch=fetch;
 this.handleFetch=handleFetch;
 this.scroll=scroll;
 this.scrollUp=scrollUp;
}

function setScrollDirection(){
 this.scrollDirection='top';
 // fire up the scrollers!
 window.setTimeout(this.scroll.bind(this),this.scrolltime);
}
function scroll(){
    this.scrollUp();
    if(this.scrollDirection!=null){
        window.setTimeout(this.scroll.bind(this),this.scrolltime);
    }
}

function scrollUp(){    
    this.overlap[2]=this.overlap[2]-this.scrollby;
    this.overlap[0]=this.overlap[0]+this.scrollby;

    this.viewport[2]=this.viewport[2]+this.scrollby
    this.viewport[3]=this.viewport[3]+this.scrollby;

    this.map.style.top=parseInt(this.map.style.top)-this.scrollby+'px';
    if(this.overlap[2]<33){
        this.destroy(this.viewport[0]-64,this.viewport[1]+64,this.viewport[2]-64,this.viewport[2]);
        this.overlap[0]=this.overlap[0]-64;
        this.fetch(this.viewport[0]-64,this.viewport[1]+64,this.viewport[3],this.viewport[3]+64);
        this.overlap[2]=this.overlap[2]+64;
    }   
    return true;
}

// globale functie. (niet compleet maar ok)
function createRequestObject(){
    var request_o; //declare the variable to hold the object.
    var browser = navigator.appName; //find the browser name
    if(browser == "Microsoft Internet Explorer"){
        /* Create the object using MSIEs method */
        request_o = new ActiveXObject("Microsoft.XMLHTTP");
    } else {
        /* Create the object using other browsers method */
        request_o = new XMLHttpRequest();
    }
    return request_o; //return the object
}

function fetch(startX,endX,startY,endY){
    this.xmlhttp = false;
    this.xmlhttp=createRequestObject();
    // cook up which range we need to get.
    var url = "../xml/tiles.php?startX="+startX+"&endX="+endX+"&startY="+startY+"&endY="+endY+"&waterlevels="+getsetting(this.quality,'water_levels')+'&watertransparancy='+getsetting(this.quality,'water_transparancy');
    //window.open(url);
    this.xmlhttp.open("GET", url, true);
    this.xmlhttp.onreadystatechange = attachEventHandler(this, "handleFetch");
    this.xmlhttp.send(null);
    return true;
}

function handleFetch(){ 
    if(this.xmlhttp.readyState == 4 && this.xmlhttp.status == 200) {
        var response=this.xmlhttp.responseXML;
            
        var tiles=response.getElementsByTagName("tile");
        var tilescount=tiles.length;
        for(var i=0;i<tilescount;i++){
            this.addtile(tiles[i].attributes[0].nodeValue,
                    tiles[i].attributes[1].nodeValue,
                    tiles[i].attributes[2].nodeValue,
                    tiles[i].attributes[3].nodeValue,
                    tiles[i].attributes[4].nodeValue,
                    tiles[i].attributes[5].nodeValue,
                    tiles[i].attributes[6].nodeValue
                    );
        }
        

    }
}


Niet alle code en vars zijn aanwezig, maar you'l get the idea.

Ik heb de allerlei events die gebeuren, maar allemaal wel binnen de scope van de class moeten gebeuren.
Daarnaast zijn er allerlei xmlrequests nodig die ook binnen de scope moeten worden gedaan, en eventueel gequeued moeten kunnen worden.

Ik zoek een elegante oplossing, maar ben een beetje verzand geraakt in de complexiteit van het probleem.

openkat.nl al gezien?


Verwijderd

Kun je het aantal cr's niet bijhouden in een superklasse o.i.d.?
Ik weet niet hoe goed js het uitbreiden van klassen ondersteunt, maar misschien heb je hier iets aan: http://dean.edwards.name/weblog/2006/03/base/.
Anders kun je mijns inziens toch ook met een globale variabele werken die vermeerdert of vermindert als er requests worden gedaan of beeindigd?

[ Voor 5% gewijzigd door Verwijderd op 11-05-2006 12:00 ]


  • BtM909
  • Registratie: Juni 2000
  • Niet online

BtM909

Watch out Guys...

Is je probleem niet gewoon het feit dat je een opslagmethode zoekt om je verschillende objecten netjes bij elkaar te houden? Daar zijn wel meerdere methoden voor te vinden, volgens mij hebben we daar zelfs een paar interessante topics over gehad, zal even proberen te zoeken.

Daarnaast snap ik je probleem niet zo met concurrent connections. Heb je echt zulke complexe applicaties dat je meer dan 1-2 requests tegelijkertijd moet doen?

Ace of Base vs Charli XCX - All That She Boom Claps (RMT) | Clean Bandit vs Galantis - I'd Rather Be You (RMT)
You've moved up on my notch-list. You have 1 notch
I have a black belt in Kung Flu.


  • killercow
  • Registratie: Maart 2000
  • Laatst online: 20-02 08:34
BtM909 schreef op donderdag 11 mei 2006 @ 12:05:
Is je probleem niet gewoon het feit dat je een opslagmethode zoekt om je verschillende objecten netjes bij elkaar te houden? Daar zijn wel meerdere methoden voor te vinden, volgens mij hebben we daar zelfs een paar interessante topics over gehad, zal even proberen te zoeken.

Daarnaast snap ik je probleem niet zo met concurrent connections. Heb je echt zulke complexe applicaties dat je meer dan 1-2 requests tegelijkertijd moet doen?
Mja, wel zo'n beetja ja, ik hoopte dat iemand hier toevallig al eens nagedacht had over een mooie ajax pooler. (zeg maar).

m'n app kan zeker wel meer dan 2 requests tegelik nodig hebben. (check http://pc-gamers.com/webgamex/iso_js_coords.php), of in ieder geval wil ik het netjes opgelost hebben in plaats van maar gewoon gokken dat de user niet ergens op klikt tijdens 2 lopende requests.

Juist omdat het kan dat er meer 2 requests lopen moet je het goed oplossen, anders krijg je allemala vage bug, van halve transacties etc die je niet goed kunt her produceren omdat je dan alles stap voor stap doet.

openkat.nl al gezien?


  • killercow
  • Registratie: Maart 2000
  • Laatst online: 20-02 08:34
oke, probleem is NOG niet opgelost :P

Even wat nieuwe gedachten.

Als je een ajax query doet kun je rustig een private var gebruiken, en dan via
JavaScript:
1
xmlhttp.onreadystatechange=function(){ xmlhttp.responseXML; }

de xml uitlezen.

Nu gaat dit helemaal goed, tenzij de response aan een bepaald obejct moet worden toegevoegd, want dan moet je op een of andere manier de object scope weten te behouden.

Nu gebruik ik dus de volgende code voor:
JavaScript:
1
2
3
4
5
function attachEventHandler(oObject, sMethodName) {
   return function () {
      return oObject[sMethodName].apply(oObject, arguments);
   };
}


(dit stukje code komt uit prototype.lite.js)

icm:
JavaScript:
1
this.xmlhttp.onreadystatechange = attachEventHandler(this, "handleFetch"); 


waarbij dus handlefetch binnen de scope van het obejct uitgevoerd wordt, en via this.xmlhttp gewoon de onreadystate en responseXML kan lezen.

Prachtig.

Maar wat nu als er meer ajax handlers in een object tegeleik gaan queryen, dan gaan ze dus elkaars this.xmlhttp overschrijven, omdat deze globaal is voor het hele object.

Ik zit er nu al zo lang mee te klooien, dat het duidelijk is dat ik iets niet helemaal begrijp of goed doe.

Ik wil dus eigenlijk een array van xmlhttp objects, binnen de class, maar als ik dat doe raak ik via
JavaScript:
1
this.xmlhttp.onreadystatechange = attachEventHandler(this, "handleFetch"); 


De pointer naar m'n array object kwijt, en weet ik dus alsnog niks.
Hoe krijg ik dit in gods naam voor elkaar?

Ik had begrepen dat ik beter geen reference naar de class (this) tijdens de originele aanmaak van het xmlrequest object in de array kan gooien, omdat dat memmory leaks geeft toch?

dit bedoel ik daarmee:
JavaScript:
1
2
3
this.ajaxhandlers[this.ajaxhandlers.length]=new Array(newajaxobject,this);
this.ajaxhandlers[this.ajaxhandlers.length-1][0].open(url,"GET");
this.ajaxhandlers[this.ajaxhandlers.length].send(null);

Please help me :P,. ik begin echt gevoel te krijgen dat dit niet kan ofzo.

openkat.nl al gezien?


  • crisp
  • Registratie: Februari 2000
  • Laatst online: 01:18

crisp

Devver

Pixelated

Waarom maak je xmlhttp en een referentie naar je object niet beide property van een overkoepelend object?
Een andere optie zou zijn een referentie naar je object op te slaan in een property van xmlhttp, maar ik weet niet in hoeverre dat werkt in de non-native XMLHttpRequest implementatie in IE <= 6 en de not-so-native implementatie in IE7 (waar het ws een COM wrapper is, recept voor memory-leaks dus).

Intentionally left blank


  • killercow
  • Registratie: Maart 2000
  • Laatst online: 20-02 08:34
Crisp,

daar zat ik ook aan te denken, ik heb even gespeeld met een los ajax object, welk gebruikt wordt door alle andere classes in het script (en zo dus voor de hele browser een limiet aan het aantal concurrent querys kan geven)

In die classe wilde ik dan inderdaad een array van xmlhttp objecten, en parent object references stoppen, maar hoe execute ik dan de functie in dat obejct?

Stel dat je het zo doet:

Een class ajax.
Met daarin een array met de volgende elements:
xmlhttprequest object,parent object reference, execute function.

Als ik dan via een method van het ajax object de queries toevoeg, geef ik de volgende gegevens mee: url,type,this/object,return functie.,

In het ajax obejct stop ik alles in de que, en haal ik telkens de eerste eruit om te verwerken.

Als xmlhttp.onreadystatechange geef ik dan wederom een reference naar het ajax object meer, zodat de functie die uitgevoerd wordt binnen die scope van dat object zit met:
attachEventHandler(this, "handleFetch")

ajax.handlefetch handelt daarna de state af, en gooit hem eventueel uit de array.

nu bleef mijn probleem hiermee:
Welk object van de array hebben we het op dat moment over? (ik heb geen idee hoe ik dit binnen de handleFetch functie krijg.

En hoe roep ik nou de functie aan die in de array opgeslagen staat, zonder een eval te doen? en pointer naar het object staat in de array, maar hoe plak ik daar de string, van de functie naam achter om het uit te voeren?

Kan dit met object[functienaam](arguments) ?

Alvast bedankt.

openkat.nl al gezien?


  • crisp
  • Registratie: Februari 2000
  • Laatst online: 01:18

crisp

Devver

Pixelated

JavaScript:
1
ajax.executefunction.apply(ajax.parentobjectreference, arguments);

;)
of voor stokoude browsers:
JavaScript:
1
2
3
ajax.parentobjectreference.__temp = ajax.executefunction;
ajax.parentobjectreference.__temp(arguments);
ajax.parentobjectreference.__temp = null;

Intentionally left blank


  • killercow
  • Registratie: Maart 2000
  • Laatst online: 20-02 08:34
Ah, tus toch wel via de apply method. ik had er al wat mee gespeeld, maar kreeg vooral allerlei (voor mij nog onduidelijke foutmeldingen), vanavond nog maar even stoeien dus.

Even voor mijn beeldvorming:

Met apply bindt, je blijkbaar een bepaalde functie aan een functie naam?

Aangezien je dus ook het volgende kunt doen:
JavaScript:
1
ajax.executefunction.apply(otherobject.function, arguments);


waarbij otherobject.function dus binnen de scope van otherobject uitgevoerd word, maar aangeroepen kan worden binnen het ajax object.

Hm,, blijkbaar niet, het eerste argument van apply, kan alleen een object zijn, geen functie reference natuurlijk.

Het werkt dus bijna zoals de bedoeling is, maar nu gaan we er vanuit dat er functie "executefunction" de uit te voeren functie is. Maar het is de bedoeling dat de fanctie genoemd in de array uitgevoerd wordt, welke gewoon alleen een string is atm.

Het zou dan zo-iets moeten worden:
JavaScript:
1
ajax[functienaaminstringvorm].apply(otherobject, arguments);

[ Voor 34% gewijzigd door killercow op 22-05-2006 16:42 ]

openkat.nl al gezien?


  • crisp
  • Registratie: Februari 2000
  • Laatst online: 01:18

crisp

Devver

Pixelated

nee:

Function.apply(Object, arguments)

dat voert een functie uit in de scope van een bepaald object

Intentionally left blank


  • killercow
  • Registratie: Maart 2000
  • Laatst online: 20-02 08:34
lastig allemaal.
De info pagina's bij mozilla helpen me ook niet echt verder, of mijn hele object ontwerp is brak.

Hier een stukje van de code:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
function handlercreateRequestObject(){
    var request_o; //declare the variable to hold the object.
    if(navigator.userAgent.indexOf('Opera') != -1){
        request_o = new XMLHttpRequest();
    } else if(navigator.appName=="Microsoft Internet Explorer"){
        /* Create the object using MSIEs method */
        request_o = new ActiveXObject("Microsoft.XMLHTTP");
    } else {
        /* Create the object using other browsers method */
        request_o = new XMLHttpRequest();
    }
    return request_o; //return the object
}

function ajaxrequest(url,parentobject,returnfunction,type){
    if(type!='GET' && type!='POST'){
        type='GET';
    }
    if(this.ajaxobjects.length<this.ajaxmaxamount ){
        this.ajaxobjects[this.ajaxobjects.length]=new Array(handlercreateRequestObject(),parentobject,returnfunction,type);
        var thishandler=this.ajaxobjects[ (this.ajaxobjects.length-1) ];
        thishandler[0].open(type,url,true);
        thishandler[0].onreadystatechange=function (){
            if(thishandler[0].readyState == 4 && thishandler[0].status == 200) {
                thishandler[2].apply(thishandler[1], new Array(input,thishandler[0].responseXML) );
            }
        }
        thishandler[0].send(null);
    } else {
        this.que[this.que.length]=(new Array(url,returnfunction));
    }
    return true;
}

function ajaxhandler(){
    this.ajaxmaxamount=2;
    // set the array to hold, all the objects and status info
    this.ajaxobjects=new Array(); // should be browser wide, will come later.
    this.que=new Array();

    this.ajaxrequest=ajaxrequest;
    this.handlercreateRequestObject=handlercreateRequestObject;

}
// and the calling object
function otherobject(){ 
    this.ajaxhandler;
    this.doAjaxA=doAjaxA;
    this.executefunctionA=executefunctionA;
    this.executefunctionB=executefunctionB;
}
function executefunctionA(input){
    // do stuff in scope of "otherobject"
}
function executefunctionB(input){
    // do stuff in scope of "otherobject"
}
function doAjaxA(url){
    this.ajaxhandler.ajaxrequest(url,this,"executefunctionA","GET");
}

// setting up the whole thing.
var ajaxhandlerpointer=new ajaxhandler;
var otherobjectpointer=new otherobject;

otherobjectpointer.ajaxhandler=ajaxhandlerpointer;
otherobjectpointer.doAjaxA('http://www.test.nl');


Nu moet op readystatechange=4 "executefunctionA" uitgevoerd worden binnen de scope van "otherobject".

Waar kan ik hier trouwens meer over lezen? want alleen aan crisp vragen is natuurlijk niet echt een oplossing :)

[ Voor 9% gewijzigd door killercow op 22-05-2006 16:54 ]

openkat.nl al gezien?


  • crisp
  • Registratie: Februari 2000
  • Laatst online: 01:18

crisp

Devver

Pixelated

Opzetje uit de losse pols:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
if (!window.XMLHttpRequest)
{
    window.XMLHttpRequest = function()
    {
        var types = [
            'Microsoft.XMLHTTP',
            'MSXML2.XMLHTTP.5.0',
            'MSXML2.XMLHTTP.4.0',
            'MSXML2.XMLHTTP.3.0',
            'MSXML2.XMLHTTP'
        ];

        for (var i = 0; i < types.length; i++)
        {
            try
            {
                return new ActiveXObject(types[i]);
            }
            catch(e) {}
        }

        return undefined;
    }
}

function AjaxPool()
{
    this.maxConcurrentRequests = 2;
    this.pool = [];
    this.queue = [];
}

AjaxPool.prototype =
{
    addRequest: function(url, type, handler, object)
    {
        if (!object) object = window;
        if (!handler) handler = function() {};
        if (!type) type = 'GET';
        else
        {
            type = type.toUpperCase();
            if (type != 'GET' && type != 'POST')
                type = 'GET';
        }

        this.queue.push(
            {
                url: url,
                type: type,
                handler: handler,
                object: object
            }
        );

        this.checkQueue();
    },

    checkQueue: function()
    {
        var i = 0;
        do
        {
            if (!this.pool[i] || this.pool[i].busy == false)
            {
                var request;
                if ((request = this.queue.shift()))
                {
                    var self = this;
                    var slot = this.pool[i] =
                    {
                        xmlhttp: new XMLHttpRequest(),
                        busy: true
                    }

                    slot.xmlhttp.open(request.type, request.url, true);
                    slot.xmlhttp.onreadystatechange = function()
                    {
                        if (slot.xmlhttp.readyState == 4 &&
                            (!slot.xmlhttp.status ||
                                (slot.xmlhttp.status >= 200 && slot.xmlhttp.status < 400)))
                        {
                            request.handler.call(request.object, slot.xmlhttp.responseXML);
                            slot.busy = false;
                            self.checkQueue();
                        }
                    }

                    slot.xmlhttp.send(null);
                }

                break;
            }
        }
        while (++i < this.maxConcurrentRequests);
    }
}

function testHandler(xml)
{
    alert(xml);
}

var myAjax = new AjaxPool();
myAjax.addRequest('http://localhost/test.xml', 'GET', testHandler);

Intentionally left blank


  • killercow
  • Registratie: Maart 2000
  • Laatst online: 20-02 08:34
ga t morgen meteen proberen,
(en wat schrijf jij toch onwijz gave JS!)

* killercow kuffels Crisp :P

(ik zat zelf ook al naar call te kijken, maar kon me geen beeld vormen van wat het nou Precies moest doen.)

openkat.nl al gezien?


Verwijderd

killercow schreef op dinsdag 23 mei 2006 @ 00:40:
ga t morgen meteen proberen,
(en wat schrijf jij toch onwijz gave JS!)
Ik moet toegeven, dat ziet er errug netjes uit! Ik heb alleen een enorme hekel aan do/while-loops, vraag me niet waarom. Ook mis ik nog de mogelijkheid om data mee te senden:
JavaScript:
1
2
var data = 'a=1&b=2';
slot.xmlhttp.send(data);

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 01:18

crisp

Devver

Pixelated

Verwijderd schreef op dinsdag 23 mei 2006 @ 07:18:
[...]

Ik moet toegeven, dat ziet er errug netjes uit! Ik heb alleen een enorme hekel aan do/while-loops, vraag me niet waarom. Ook mis ik nog de mogelijkheid om data mee te senden:
JavaScript:
1
2
var data = 'a=1&b=2';
slot.xmlhttp.send(data);
Het is ook niet bedoelt als compleet script ;)
Er moet nog wel meer aan gedaan worden, in de eerste plaats het afvangen van errors. Verder een mogelijkheid om een synchroon request te versturen en inderdaad POST data.

Je ontkomt er niet aan door je pool te itereren om te kijken of er een vrij 'slot' is; of je dat nou met een do-while of met een for doet of misschien zelfs met een forEach boeit natuurlijk niet zo :P

Intentionally left blank


  • killercow
  • Registratie: Maart 2000
  • Laatst online: 20-02 08:34
De rest van de code moet wij wel lukken lijkt mij zo.

Als niemand/crisp het erg vindt, krijgt het daarna een ere plekje in freevolution. (en een credits vernoeming naar Crisp/dit topic.

En ik snap nu eindelijk waarom protoypen gebruikt wordt, ziet er een stuk beter uit dat mijn bouwsel waarin je elke functie in de class moet defineren.

Eventueel kun je trouwens ipv de while loop een iets andere oplossing gebruiken.
Zodra een slot ready is, clear je dat array element, en decrement je een variabele.
Als die variabele lager is dan maxConcurrentConn gooi je gewoon een nieuw array element aan het einde van de array erbij. En heb je dus geen loopje nodig om de gaten te vinden, want die komen vanzelf vooraan.

Of het netjes is, tjah.

[ Voor 38% gewijzigd door killercow op 23-05-2006 09:35 ]

openkat.nl al gezien?

Pagina: 1