js file_exists alternative zu XMLHttpRequest

EuroCent

Klappstuhl 2.0
Ich habe eine Frage, gibt es eine Alternative zu XMLHttpRequest.

Mein JS-Code:
Javascript:
/**
 * Prüfen ob Datei existiert
 * @param {string} url URL zur Datei
 *
 * @returns {boolean} True/False
 */
function fileExists(url) {
    var http = new XMLHttpRequest();
    http.open('GET', url, false);
    http.send();
    return http.status != 404;
}

Unter IE funktioniert es zwar, aber unter Chromium (Edge und Google Chrome) kommt die Meldung dass XMLHttpRequest veraltet ist.
Meldung: [Veraltet] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check XMLHttpRequest Standard.

Ich hatte es auch mal mit
Javascript:
fetch(url, function(res) {
    return res.status != 404;
});
 
Lösung
Javascript:
function fileExists(url) {
    fetch(url, function (res) {
        return res.status != 404;
    });
}
Das hier funktioniert deswegen nicht, weil du mit asynchronen Operationen inkorrekt umgehst. Wenn du genau hinschaust, siehst du, dass fileExists gar keinen Rückgabewert hat. Das return bezieht sich nur auf die anonyme Funktion, die du da hast -- nicht auf fileExists.

Die "Standardsemantik" von JavaScript, Code zu verarbeiten, ist immer synchron. Wenn du einmal etwas Asynchrones startest und das Ergebnis benutzen möchtest, musst du davon ausgehend alles "asychronifizieren" bis zum Toplevel.

Probier mal:
Javascript:
async function fileExists(url) {
  return fetch(url).then((res) => res.status != 404);
}

Aufruf:
Javascript:
//...
Nein, generell ist XMLHttpRequest nicht veraltet, diese Meldung bezieht sich nur auf "Synchronous":
Meldung: [Veraltet] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience.
(Obwohl ich selber das neuere fetch vorziehe.) Willst Du die Fehlermeldung vermeiden, musst Du auf asynchron umstellen, was das Handling etwas komplizierter macht, denn Du musst alle Aktionen, die die Info brauchen, im Callback durchführen.
 
Nein, generell ist XMLHttpRequest nicht veraltet, diese Meldung bezieht sich nur auf "Synchronous":

(Obwohl ich selber das neuere fetch vorziehe.) Willst Du die Fehlermeldung vermeiden, musst Du auf asynchron umstellen, was das Handling etwas komplizierter macht, denn Du musst alle Aktionen, die die Info brauchen, im Callback durchführen.
Ahh... okay. :)


Wie müsste es dann via fetch sein?

Aktuell hab Ich es so:

Javascript:
/**
* Prüfen ob Datei existiert
* @param {string} url URL zur Datei
*
* @returns {boolean} True/False
*/
function fileExists(url) {
    fetch(url, function (res) {
        return res.status != 404;
    });
}

Wenn Ich allerdings die Prüfung darauf mache:

Javascript:
if(fileExists('DATEIPFAD/DATEINAME.php')) {
    console.log("FIND");
} else {
    console.log("NOT FIND");
}

Dann bekomme Ich immer den Else zweig.

Muss ehrlich auch sagen, dass Ich mit fetch noch nicht viel bis gar nichts gemacht habe :D

EDIT:
Javascript:
/**
 * Prüfen ob Datei existiert
 * @param {string} url URL zur Datei
 *
 * @returns {boolean} True/False
 */
function fileExists(url) {
    fetch(url, {
        mode: 'no-cors',
        method: 'get'
    }).then(function (response) {
        console.log("RES:", response.status);
        var res = (response.status == 404) ? false : true;
        console.log("RES:", response.status, res);
        return res;
    }).catch(function (err) {
        console.log("ERR:", err);
    });
}

Auch mit dieser Anpassung, komm Ich nur in den ELSE-Zweig :(
 
Zuletzt bearbeitet:
Javascript:
function fileExists(url) {
    fetch(url, function (res) {
        return res.status != 404;
    });
}
Das hier funktioniert deswegen nicht, weil du mit asynchronen Operationen inkorrekt umgehst. Wenn du genau hinschaust, siehst du, dass fileExists gar keinen Rückgabewert hat. Das return bezieht sich nur auf die anonyme Funktion, die du da hast -- nicht auf fileExists.

Die "Standardsemantik" von JavaScript, Code zu verarbeiten, ist immer synchron. Wenn du einmal etwas Asynchrones startest und das Ergebnis benutzen möchtest, musst du davon ausgehend alles "asychronifizieren" bis zum Toplevel.

Probier mal:
Javascript:
async function fileExists(url) {
  return fetch(url).then((res) => res.status != 404);
}

Aufruf:
Javascript:
// Alternative 1
fileExists("http://...").then(exists => {
  // ...
  console.log(exists)
});

// Alternative 2 (following line must be in an async function itself or top-level [in a recent browser])
const exists = await fileExists("http://...");

Merke: du wirst fileExists nie mehr einfach so aufrufen können. Denn normale Funktionsaufrufe sind "synchron" in JS. Du brauchst immer entweder (a) eine Callbackfunktion (klassisches Node.js Paradigma) oder (b) eine Nutzung von Promises (löst (a) ab) oder (c) ein await-Statement (alternativ (b), Geschmackssache).

Option (c) sieht vermeintlich synchron aus, ist es aber nicht. Es zwingt dich zum Beispiel die umgebende Funktion auch als async zu markieren.
 
Lösung
Das hier funktioniert deswegen nicht, weil du mit asynchronen Operationen inkorrekt umgehst. Wenn du genau hinschaust, siehst du, dass fileExists gar keinen Rückgabewert hat. Das return bezieht sich nur auf die anonyme Funktion, die du da hast -- nicht auf fileExists.
Dafür könnte Ich mich Ohrfeigen. :D
Das habe Ich überhaupt so gar nicht betrachtet.
Aber wieder was gelernt. :)

async function fileExists(url) { return fetch(url).then((res) => res.status != 404); }

Vielen Lieben Dank für diesen Denkanstoss. :)
 
Gern! Übrigens: in Scala gibt es natürlich auch Lambdas. Und dann kannst du sowas schreiben wie:
Code:
def process(list: List[Int]): Boolean = {
  list.foreach(x => {
    if (x < 42) return false
  })
  return true
}
(Ja, das kann man idiomatischer schreiben: def process(list: List[Int]): Boolean = list.forall(_ >= 42).)

process gibt true zurück, wenn die Liste keinen Wert oder nur Werte >= 42 beinhaltet, ansonsten false.
Der Clou ist nun, dass sich returns immer auf die nächstinnerste benannte Funktion beziehen (entgegen etwa JavaScript wie im oberen Beitrag erwähnt). Daher, wenn es tatsächlich ein x < 42 gibt, so wird das natürlich im foreach traversiert und das "return false" returned für die ganze Funktion process.

In letzter Zeit habe ich gelernt, dass diese Returnsemantik tatsächlich ganz nützlich sein kann. Aber Achtung: einmal war ich von so vielen Funktionen und Syntactic Sugar umgeben, dass ich auch tatsächlich lange gesucht habe, warum meine Funktion nicht kompiliert (Type Checking Error wegen "falschem" return).

Also keine Sorge, wenn du bei dir das return auch mal falsch zuordnest :)
 
Gern! Übrigens: in Scala gibt es natürlich auch Lambdas. Und dann kannst du sowas schreiben wie:
Code:
def process(list: List[Int]): Boolean = {
  list.foreach(x => {
    if (x < 42) return false
  })
  return true
}
(Ja, das kann man idiomatischer schreiben: def process(list: List[Int]): Boolean = list.forall(_ >= 42).)

process gibt true zurück, wenn die Liste keinen Wert oder nur Werte >= 42 beinhaltet, ansonsten false.
Der Clou ist nun, dass sich returns immer auf die nächstinnerste benannte Funktion beziehen (entgegen etwa JavaScript wie im oberen Beitrag erwähnt). Daher, wenn es tatsächlich ein x < 42 gibt, so wird das natürlich im foreach traversiert und das "return false" returned für die ganze Funktion process.

In letzter Zeit habe ich gelernt, dass diese Returnsemantik tatsächlich ganz nützlich sein kann. Aber Achtung: einmal war ich von so vielen Funktionen und Syntactic Sugar umgeben, dass ich auch tatsächlich lange gesucht habe, warum meine Funktion nicht kompiliert (Type Checking Error wegen "falschem" return).

Also keine Sorge, wenn du bei dir das return auch mal falsch zuordnest :)
Ja das kenne Ich zugut
Passiert mir auch ab und an, wenn man jQuery mit Standard JS vermischt, was man nicht oder gar nie machen sollte...

Was nicht unbedingt heißt dass es nicht geht, aber aktuell versuche Ich mein Projekt von jQuery zu lösen...
Da ich allerdings teils noch Bootstrap verwende ist dies nicht ganz so einfach, weil eben Bootstrap viele kleine Dinge einem Abnimmt.

Daher bau Ich teils vieles nach in Vanilla JS um von den Abhängigkeiten weg zu kommen :)
 
Zurück