Schleifen mit Requests JS/AJAX

Hallo Ajax Community,

ich bastel mit einem Kumpel gerade an einer Website zum Schiffe versenken spielen, basiernd auf JS/AJAX. Damit beim Aktualisieren keine Schüsse verloren gehen basteln wir an einer Funktion die onLoad aufgerufen wird und mittels Request die Schüsse setzt. Das ganze sollte mit einer while-Schleife für jedes Feld durchlaufen. Anhand Firebug sehe ich, dass er die Schleife auch tatsächlich so oft durchläuft. Der Request selbst mit der Interpretation wird aber nur beim letzten mal tatsächlich ausgeführt. Die Daten werden aber jedesmal per echo über XML aus dem php-Script übergeben. Er fängt nur nichts damit an. Woran kann das liegen? Mach ich irgendwas falsch?
Zur besseren Bearbeitung hänge ich auch mal den Code an.

Dies hier ist die Funktion die beim Reload die Schüsse wieder setzt:

[CODE]function reloadShoots(){
var i = 0;
while(i als XML-Dokument
var xmlDoc = req.responseXML;
// Namen aus dem XML-Dokument herauslesen
var id = xmlDoc.getElementsByTagName(‚id‘)[0].firstChild.nodeValue;
alert(" irgendwas");
var content = xmlDoc.getElementsByTagName(‚content‘)[0].firstChild.nodeValue;

//var event = xmlDoc.getElementsByTagName(‚event‘)[0].firstChild.nodeValue;
// Namen in die Felder schreiben

document.getElementById(id).style.backgroundImage = content;
//document.getElementById(‚event‘).innerHTML = event;

}
break;
default:
break;
}
}[/CODE]

Das im Code stehende alert(" irgendwas") diente zum Überprüfen.
Vor var id … kommt es nach jeder Schleife. Nach var id … kommt es nur noch zum Schluss. Also muss hier irgendwo der Fehler liegen, oder? Genauer konnte ich es nicht eingrenzen.

Und das hier ist der Inhalt der angesprochenen php-Datei:

[CODE]<?php include("_settings.php");
include("_connect.php");
include("_functions.php");

header(‚Content-Type: text/xml; charset=utf-8‘); // sorgt für die korrekte Kodierung
header(‚Cache-Control: must-revalidate, pre-check=0, no-store, no-cache, max-age=0, post-check=0‘); // ist mal wieder wichtig wegen IE

$min = $_POST[‚min‘];
$id = „“;
$content = „“;

$result = mysql_query(„SELECT * FROM shoots WHERE game_ID = $gameID AND user_ID = $user2_ID ORDER BY shoot_ID DESC LIMIT $min,1“);
while($data=mysql_fetch_array($result)){
$id = $data[‚field‘];
$hit = $data[‚hit‘];
$loop = true;
switch($hit) {
case „1“:
$content = ‚url(gfx/bg_1_X.jpg)‘;
break;
case „0“:
$content = ‚url(gfx/bg_1_O.jpg)‘;
break;
default:
break;
}
}
echo „<?xml version=\"1.0\" encoding=\"utf-8\"?>\n“;
echo „\n“;
echo " „.„M_“.$id.“\n";
echo " „.$content.“\n";
echo „\n“;
?>[/CODE]

Die Domain lautet http://seeschlacht.co.de . Firebug gibt auch keine Fehler aus, nur das was in der PHP als echo steht.

Würd mich über jede Antwort freuen, denn wir stehen hier momentan vor einem Rätsel.
Wir beide erlernen JS/AJAX auch erst gerademal seit einer Woche, also bitte nicht in totale Fachsprache verfallen beim Antworten :stuck_out_tongue:.
Danke schonmal an alle!!

Gruß
Lunacy

Sorry Leute,

ich habe gerade zu viel um die Ohren…

generell wuerde ich Euch aber von Php abraten und Python benutzen… (hilft Euch momentan aber auch nicht weiter.)

Viel Glueck

Max

Hallo,

dein Fehler ist beim Erzeugen der Folgerequest. Ihr erzeugt einen Request, der abgeschickt wird. Die Antwort wird aber asynchron geliefert… d.h. es dauert etwas, bis die Antwort ankommt und verarbeitet werden kann. Das soll von er Funktion erledigt die ihr mit req.onreadystatechange setzt…

Noch etwas… Die folgende Zeile sollte unbedingt -vor- dem „req.send();“ stehen:

>> req.onreadystatechange = function(){interpretReq(req)};

=> den Grund dafür könnt ihr unten nachlesen…

Das Problem ist, dass Ihr in der Schleife jedes mal den letzten Request „überschreibt“ mit dem neuen Request, weil ihr die selbe Variable zum speichern der Referenz auf das Objekt benutzt. Und dann werden (wenn ihr Glück habt) die Anfragen zwar an den Server geschickt, aber das Objekt ist schon „freigegeben“ worden, wenn die Antwort fertig wäre…
Ihr müsst also dafür sorgen, dass eine Referenz auf das Request-Objekt erhalten bleibt, bis der Request fertig ist. Eine einfach Lösung wäre, die Requests in einem Array zu speichern und dann zu entfernen, wenn die Verarbeitung abgeschlossen ist (d.h. entweder der Request fehlgeschlagen ist oder erfolgreich abgeschlossen wurde). Besser ist, wenn ihr den Request nur dann nochmal sendet, wenn er fehlschlägt…
Dafür legt ihr den Request wie bisher an und in der interpretReq()-Funktion sehr ihr dann, obs geklappt hat… die wird irgendwann aufgerufen. Wenn euch eine Antwort auf einen fehlschlagenden Request zu lang ist, solltet ihr parallel einen Timer starten und dann ggf. den Request abbrechen und einen neuen erzeugen.

Ich hab ein wenig das Gefühl, dass ihr noch nicht ganz verstanden habt, was „Asynchrone“ Requests sind…

Deswegen kurz die Erklärung dafür:
Wenn ihr den Request erzeugt habt und mit „send“ in Auftrag gegeben habt, läuft die Verarbeitung im Hintergrund ab. D.h. das Programm läuft sofort an der nächsten Zeile weiter, ohne dass auf das Ergebnis gewartet wird. Es bringt aber nichts in einer Endlosschleife darauf zu warten, weil die „Hintergrundprozesse“ nur ausgeführt werden, wenn grad nichts zu tun ist. Der Interpreter kümmert sich immer nur um eine Sache (z.B. den Teil des Quellcodes mit der Schleife)… d.h. wenn diese Schleife fertig ist (und der Javascriptblock zuende ist) und nicht anderes zu tun ist, verarbeitet er „Events“. D.h. Signale wie „da ist jetzt die Verbindung aufgebaut worden“ oder „der Nutzer hat dort auf den Button geklickt“ und ruft die Funktionen auf, die man durch setzen der Eventhandler-Funktionen (alles was so mit „on…“ anfängt) eingestellt hat. Jedoch immer der Reihe nach. Also erst wenn eine Eventhandler-Funktion beendet wurde, kann das nächste Event verarbeitet werden.

Ich hoffe das hilft euch weiter. Viel Erfolg…

LG
Stefan

Ok … kein Ding!
Gruß

Hallo Stefan,

danke für die schnelle Hilfe. Aber ganz ehrlich … vom ersten part hab ich fast nichts kapiert. Das mit den Arrays und so. Kannst du das evtl. noch mal näher ausführen?
Der zweite Teil ist verständlich.
Danke nochmal.
Gruß
Marco

Sorry dabei kann ich nicht helfen, bitte wende dich an einen anderen experten

  1. die Funktion die das „onReadyStateChange“-Event verarbeitet solltet ihr -vor- dem „send()“-Aufruf setzen das ist die eine Zuweisung „req.onreadystatechange = function(){interpretReq(req)};“. Grund: Bereits beim aufrufen von „send()“ könnte diese Funktion (auch „Callback Function“ genannt) aufgerufen werden. Wenn dann noch keine Funktion eingestellt ist, bekommt ihr keinen aufruf an Eure funktion. Also -vorher- alle onXYZ-Zuweisungen erledigen und dann den Request „starten“ (abschick mit "send()).

Der andere Teil hat etwas mit der Speicherverwaltung zu tun. Ein Objekt wird mit „new Objektname(…)“ erzeugt. Das Ergebnis dieses „Aufrufs“, ist eine „Referenz“ auf dieses Objekt. D.h. eine Variable, die genau dieses Objekt referenziert. In anderen Programmiersprachen gibt es „Pointer“ (zu Deutsch „Zeiger“)… Nur über eine solche Referenz kann man auf ein Objekt zugreifen. In Eurem Quellcode ist „req“ also eine Variable die eine „Referenz“ auf ein Objekt (oder auch „Instanz“) vom Typ XMLHttpRequest speichert.

Zur objektorierten Programmierweise:

Um den Begriff „Instanz“ und „Typ“ zu erklären: Nehmen wir als „Typ“ oder die „Klasse“ das Beispiel „Hund“ aus dem realen Leben. Der Hund hat als Klasse bestimmte Eigenschaften… z.B. vier Beine, zwei Ohren, ein Herz, ist ein Tier; Wobei „Tier“ hier wieder eine Klasse (genauer eine „Oberklasse“ = sinngemäß „Überbegriff“) ist. Ein „Typ“ bzw. eine „Klasse“ beschreibt also etwas. Aber es existiert evtl. noch garnicht oder kann viele male existieren… Eine Instanz ist dann eine konkrete Umsetzung eines Objekts einer bestimmten Klasse. Zum Beispiel: du hast zwei Hunde. Sie sind beide „Hunde“ und sie sind keine „Katzen“, aber beide ein „Tier“. Wenn ein Hund „stirbt“ (=Objekt wird freigegeben) dann ist es nicht mehr da. Aber der andere ist noch da. Ein Hund kann auch z.B. älter sein als ein anderer Hund… d.h. es gibt die gemeinsame Eigenschaft „Alter“, aber jede „Instanz“ (= jedes Individuum) hat hier seinen eigenen Wert, der sich individuell verändern kann. Wenn ein Objekt „freigegeben“ wird, kann damit nicht mehr „gearbeitet“ werden. Damit man nun auf ein bestimmtes Objekt zugreifen kann, benötigt man eine „Referenz“ auf genau dieses Objekt. Diese bekommt man, wenn das Objekt erzeugt wird. Man kann sie speichern und auch kopieren…

Speziell zu JavaScript:

Wenn die letzte Referenz (d.h. auch eine „Kopie“ der ursprünglichen Referenz zählt dazu) auf ein Objekt überschrieben (z.B. req = false:wink: oder freigegeben (z.B. wenn die Variable nur als lokale Variable angelegt wird und der „Gültigkeitsbereich“ dieser Variable endet), wird das Objekt freigegeben.

Fazit:

Ihr müsst also dafür sorgen, dass ihr die Referenz auf Eure Request-Objekte so speichert, dass 1. der Gültigkeitsbereich nicht verlassen wird (also als globale Variable) und 2. die Referenz nicht einfach überschrieben wird (z.B. durch ein Array statt nur einer einfachen Variable).
Eine globale „Variable“ bekommt ihr recht einfach und zuverlässig, indem ihr „document.Variablenname = …;“ benutzt. Damit fügt ihr dem HTML-Dokument eine weitere Eigenschaft hinzu… das Objekt „document“ existiert normalerweise bis die Webseite im Browser geschlossen wird. D.h. da gibts keine Probleme, dass die Variable gelöscht wird.
Zum Beispiel:

document.requests = new Array(); // globales Array erzeugen

for(i=0; i

Hallo,
Lunacy,
ich hab den quellcode nicht ganz durchgelesen würde aber eine design änderung empfehlen.

so wie ich das sehe sendest du 5 AJAX request in deinem while loop das ist aber ungünstig. das das hertellen einer HTTP verbindung relativ lange dauert. deswegen währe es günstier einen einzigen request zu machen der dann aller ergebnisse zurück gibt.

vorzugsweise als json ( Javascript Object Notification }

ungefähr so
{
0 {
posX = 3
posY = 4
state = water
}
1 {
posX = 5
posY = 6
state = hit
}
}

JSON kann mit läst sich in javascript mitels eval in eine Javascript object verwandeln und man kann den ganzen status des spielfeldes in einem einzigen AJAX Request abdecken.

ich persönlich würde auch immer eine Javascript Libary für die AJAX geschichten empfehlen weil das einem viele probleme mit verschiedenen browsern abnimmt.

ich verwende geren jQuery - write less do more

zum debuggen würde ich lieber
console.log(„nachricht“); verwenden. zumindest solange man im firefox testet. dort gibt es mit firebug ein wunderbares werkzeug zum debugen von webseiten.

ich hätte da noch einen wichtigen hinweis!!!
ihr habt in eurem PHP code die möglichkeit einer SQL injection damit könnte jeder euren SQL server übernehmen!!

es ist wichtig jegliche eingaben die man von aussen bekommt. zu validieren. damit nur „sinvolle“ parameter genommen werden. im konkreten falls setzt ihr die $gameID einfach so in ein SQL statement ein.

wenn ich jetzt als game id folgendes übergeben würde

?gameID=1; DROP table shoots; #

würde ich euch mal so eben die tabelle mit den schüsse löschen.

wenn ihr erwartet das $gameID ein integer ist
macht bitte ein intval($gameID) dann habt ihr auch sicher einen integer.

falls gameID etwas anderes ist. sollte es in jedemfall über mysql_real_escape_string() geschickt werden bevor es in irgendwelche datenbank querries geht.

ähnliches gilt überings auch für sachen die man auf der webseite ausgibt die der user eingegeben hat. die sollten auch bereinigt werden.

sollt setzen die user vieleicht script tags die z.b maleware herunter laden und ihr verteilt die dann fröhlich ohne es zu wissen.

gruss chris

gruss chris

Sorry, da bin ich überfragt. RL

Hab das ganze jetzt schon etwas besser verstanden. 100% noch nicht … aber das macht nichts. Hab das ganze jetzt gelöst.
Verwirkliche das nun mit JSON. Das geht auch deutlich schneller, weil ich jetzt nur noch einmal nen Request starten muss und das ganze in der php-Datei als Array übergeben wird und anhand JSON ausgelesen wird … funktioniert super.
Trotzdem Danke für deine tolle Hilfe!!!

Sorry dabei kann ich nicht helfen, bitte wende dich an einen
anderen experten

Kein Ding. Danke trotzdem. Das Problem ist mittlerweile gelöst!

Hallo Chris,

dein Tip mit JSON war super. Hab das ganze damit einwandfrei und sehr schnell zum laufen gebracht. Vielen vielen Dank!!

Normalerweise dürfte man da nichts einschmuggeln können. Sowohl die $gameID als auch die $user2_ID sind globale Variablen die im Header anhand einer Funktion direkt aus der Datenbank ausgelesen werden. Sie werden weder mit POST noch mit GET übergeben. Also dürfte da doch nichts sein, oder??
Auf jedenfall danke auch für den Hinweis.

Gruß
Marco

Hi Marco,
wenn die variablen tatsächlich direct aus der DB kommen ist da keine grosse gefahr. wichtig ist nur jeden user input zu validieren. (sicher ist sicher)

gruss chris

Hallo Lunacy,
ich hätte da so einen ersten Verdacht und der geht in Richtung PHP-Script. In Deiner Code-Schleife schreibst Du :

…
case "1":
 $content = 'url(gfx/bg\_1\_X.jpg)';
 break;
case "0":
 $content = 'url(gfx/bg\_1\_O.jpg)';
 break;
…

was dafür sorgen müsste, das Deine Variable $content bei jedem Schleifendurchlauf neu belegt wird, also am Ende der Schleife nur den Letzten Wert beinhaltet.
Änder Deine Schleife mal um in :

...
case "1":
 $content = $content."url(gfx/bg\_1\_X.jpg)\n";
break;
case "0":
 $content = $content."url(gfx/bg\_1\_O.jpg)\n";
break;
… 

Dadurch must Du natürlich die Zeile im HTTPRequest auch anpassen:

…
echo " ".$content."\n";
…

zu

…
echo $content;
…

Ich denke, so sollte es gehen.
Ich hoffe, das es hilft (und das ich den CODE richtig interpretiert hab *G*), sag bitte grad bescheid dann.

So long,
Burkhard

Hallo,

sorry für die Verspätung. Ich kann auf den ersten Blick keine Schwachpunkte erkennen, mit einem Login zum Testsystem aber könnte ich den Request untersuchen. Geht das?

Viele Grüße

Alex

Hallo Alex,

danke noch für deine Antwort. Habs jetzt selber total verpennt, da das Problem gelöst ist. Schuld war die Schleife die den Request immer wieder überschrieben hat, mithilfe JSON lässt sich das Problem lösen.

Gruß
Marco