JavaScript SetTimeout loop - erst Code ausführen, dann timeout

CitroenDsVier

Lt. Commander
Registriert
Dez. 2013
Beiträge
1.896
Hallo zusammen,

ich habe folgenden Codeschnipsel:

Code:
var refresh = function() {
	while (true) {
		setTimeout(function(){
			// send ajax
			var xmlhttp = new XMLHttpRequest();
			xmlhttp.onreadystatechange = function() {
				if (this.readyState == 4 && this.status == 200) {
					$("#module-box").html(this.responseText);
				}
			};
			xmlhttp.open("GET", "test.php", true);
			xmlhttp.send();
		}, 5000);
	}
}

$(document).ready(refresh);

Mein Ziel ist es, nach dem Laden der Seite den ersten AJAX-Request durchzuführen, dann 5sek warten, wieder AJAX usw.

Ich weiß, dass setTimeout() den darin enthaltenen Code verzögert. Ohne darin enthaltenen Code will setTimeout aber nichts tun (z.B. while true ( AJAX; setTimeout(5sek) ))...

Wegen des while(true)-loops will die Seite nun aber garnicht zu Ende laden (Firefox meldet "eine Seite verlangsamt Ihren Computer"). Ohne die while(true)-Sache wird natürlich nicht geloopt.

Was kann man da tun?


Nachtrag: Natürlich ist die while(true) Sache keine Lösung, jetzt fällts mir auf. Da werden parallel ja 1000e refresh() Methoden aufgerufen... Gibts da ne Alternative zu while?
 
Zuletzt bearbeitet: (Nachtrag)
Hallo,

den folgenden Code irgendwo in das "DOM-Ready-Gebilde" geworfen:

Code:
function loopedFunction() {
    console.log("ausgeführt ...");
}

loopedFunction();
setInterval(loopedFunction, 5000);


Wird einmal am Anfang aufgerufen und dann in der Folge alle 5 Sekunden.
 
Eine huebschere Loesung waere es einen neuen Request erst abzuschicken wenn die Antwort vom Server eingegangen ist. So hat man die Moeglichkeit auf Fehler zu reagieren und ggf. die Anfragen zu drosseln oder ganz zu stoppen. Wenn man anstatt alle 5 Sekunden z.B. alle 100ms etwas abfragen moechte, die Anwort vom Server aber 120ms benoetigt, wuerde man mit setInterval recht schnell den Server flooden. ;)

Code:
function loopedFunction() {
    // send ajax
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
            // naechster Request
            setTimeout(loopedFunction, 5000);
            $("#module-box").html(this.responseText);
        }
        if(this.readyState === XMLHttpRequest.DONE && this.status !== 200) {
            // Server hat sich verschluckt, mal ein bisschen laenger warten
            setTimeout(loopedFunction, 10000);
        }
    };
    xmlhttp.open("GET", "test.php", true);
    xmlhttp.send();
}
 

$(document).ready(function() {
    loopedFunction();
});
 
Ich arbeite mittlerweile nur noch mit Promises und async/await sowie TypeScript und würde das so lösen:

Code:
async function sleep(ms: number): Promise<void> {
    return new Promise<void>((resolve, reject) => setTimeout(() => resolve(), ms));
}

function setModuleBox(data: string) {
    const moduleBox = document.getElementById('module-box');

    if (moduleBox == null) {
        throw new Error('module-box not found');
    }

    moduleBox.innerHTML = data;
}

async function getData(): Promise<string> {
    const response = await fetch('test.php');
    const data = await response.text();
    return data;
}

async function updateModuleBox() {
    try {
        const data = await getData();
        setModuleBox(data);
    } catch (error) {
        console.error(error);
        alert((error as Error).message);
    }
}

async function updateModuleBoxLoop(delay: number) {
    while (true) {
        await updateModuleBox();
        await sleep(delay);
    }
}

updateModuleBoxLoop(5000);
(besser direkt hier anschauen: https://www.typescriptlang.org/play/index.html#src=async...AupdateModuleBoxLoop(5000))

Alternativ mit reinem JavaScript und dann Babel, dass der Code auch unter IE läuft: http://babeljs.io/repl/#?babili=false&browsers=ie%206&build=&builtIns=false&code_lz=IYZwngdgxgBAZgV2gFwJYHsIxAGwKZ4AOAFALYgCUMA3gFAwMwBOeyCTWEeA7jAApN0pVCDzFiLEOhwA3PABpmeAFZ4oyKgF4AfNlYAVVKTzoEycVt2Tpc4hUXkKFANy0AvrVqIUGLKOQAsugAJgj4AELoAB7EwcDIwFR0jDBQmCDIMKQhYXiRUTCaMMHoUAjGEMgAdADmrACi-BXI4WAAksHEAOTZofgAtABG0V0unimocDBkORHRhUUQYThJ9CmMyAAWgrxcvPVMgkzdvblD8xDomXCmEMGjzuswawweDC9Zs3nRVagQXEwABL6AIAGUKxXiwFcHlooEgsG86l8MDqyAAIlC7DQPmkIBklCBCOk8BDgNxgKhrqwoJtusg8BkqoRNoQHrj0pk4gkyRSqYTifi8FUGVFzGMUiw2BxIQkYZ54dB4EhkZgYAhCNy8EE-t8YqsUsgmGAcU8GHiCdzgLzKZk0ZiEnZXGa9IEvvlYlCJYw3Kl4rTpnhDugmAazRbpMKg0diNGQ96nsB8ExzHGmFVjCAQMA6gmYB5YYrESq0GqNVqdbl8qD0OgSME8DhgGAwwxuJtUPhpkaEHhW-tybb1Zr4tr3dEnR8UoP-bgCPXG828wXPOXR5W5lEa3XiABWAAMh5cQA&debug=false&circleciRepo=&evaluate=false&lineWrap=false&presets=&targets=&version=6.26.0 (unter Env Preset den Haken bei Enabled setzten)


Außerdem würde wohl noch ein Polyfill für Fetch fehlen: https://github.com/github/fetch - alternativ mit XMLHttpRequest ersetzen.



Edit:
bei der untersten Funktion habe ich beim Aufruf von updateModuleBox das await vergessen (mittlerweile ausgebessert):

Code:
while (true) {
        await updateModuleBox();
        await sleep(delay);
    }
 
Zuletzt bearbeitet:
Die 5 sek sind in der Tat nicht das Optimum, das wäre ein Intervall von 0,95 bis 1sek.

@r15ch13: Deine Lösung liest sich ganz gut, aber da fehlt doch das loopen oder? Sehe ichs nur nicht?

@Bagbag: Das sieht nach einer schönen Lösung aus, wobei ich die Syntax in der sleep-Methode noch nicht ganz durchschaut habe... Werde ich mich hinterklemmen.

Ich habe das Problem jetzt mit setInterval gelöst und vorher meine Methode nochmal "manuell" aufgerufen:

Code:
var sendAjax = function() {
	// send ajax
	var xmlhttp = new XMLHttpRequest();
	xmlhttp.onreadystatechange = function() {
		if (this.readyState == 4 && this.status == 200) {
			$("#module-box").html(this.responseText);
		}
	};
	xmlhttp.open("GET", "test.php", true);
	xmlhttp.send();
	console.log("ajax request sent");
}

$(document).ready(function() {
	sendAjax();
	setInterval(function(){
		sendAjax();
	}, 1000);
});

Vielen Dank für Eure Antworten!
 
Ein Funktion die sich mit setTimeout selbst ausfuehrt ist doch ein loop oder nicht? :D
 
ahja, okay. Hatte ich wohl überlesen...

Die Variante hab ich selbst bereits probiert gehabt, hatte zur Folge, dass Firefox in der Konsole gemeldet hat "too much recursion" oder so. Ich vermute den Grund darin, dass die "erste" Funktion nie beendet wird, sondern vorher sich selbst nochmal ("zweites mal") startet. Die "zweite" Funktion wird ebenfalls nicht beendet, sondern startet vorher die dritte, usw.

;)

Mit setInterval() wird jede aufgerufene Funktion auch beendet. Daher für mich die bessere Lösung.
 
Vielleicht verstehst du es so besser:

Code:
function sleep(ms) {
    const delayedResolveFunction = createDelayedResolvePromiseFunction(ms);
    return new Promise(delayedResolveFunction);
}

function createDelayedResolvePromiseFunction(ms) {
    return function (resolve) {
        setTimeout(resolve, ms);
    }
}

Wenn du das in die Entwickler-Console eingibst, kannst du danach folgendes machen:
Code:
## Chrome 62
await sleep(3000);
console.log('hello');

#Sonstige halbwegs moderne Browser:
(async () => {
   await sleep(3000);
   console.log('hello');
})();
Und siehst dann 1., dass die Console/der Code dadurch nicht blockiert wird (kannst also gleich weiter was reinschreiben) und 2., dass der Ablauf des des Codes, trotzdem synchron ist (also das console.log erst nach dem sleep kommt).
 
Zuletzt bearbeitet:
Zurück
Oben