Perfekte Lösung für asynchrone Timeout-Vorgänge im JavaScript-Frontend

Perfekte Lösung für asynchrone Timeout-Vorgänge im JavaScript-Frontend

Seit der Veröffentlichung der Funktionen Promise ES2015 und async/await ES2017 von ECMAScript ist Asynchronie in der Front-End-Welt zu einer besonders häufigen Operation geworden. Es gibt einige Unterschiede in der Reihenfolge, in der asynchroner und synchroner Code Probleme verarbeiten. Das Schreiben von asynchronem Code erfordert ein anderes „Bewusstsein“ als das Schreiben von synchronem Code.

Was passiert, wenn die Ausführung eines Codes lange dauert?

Handelt es sich um synchronen Code, kommt es zu einem Phänomen namens „Nichtreagieren“ oder, um es in Laiensprache auszudrücken, „Tod“. Was aber, wenn es sich um asynchronen Code handelt? Möglicherweise erhalten wir das Ergebnis nicht, aber anderer Code wird fortgesetzt, als wäre nichts passiert.

Natürlich ist es nicht so, dass die Sache nicht wirklich passiert wäre, es treten nur unter unterschiedlichen Umständen unterschiedliche Phänomene auf. Beispielsweise sieht eine Seite mit einer Ladeanimation so aus, als würde sie ständig geladen. Ein anderes Beispiel ist eine Seite, die Daten aktualisieren sollte, bei der Sie die Datenänderung jedoch nicht sehen können.

Beispielsweise kann ein Dialogfeld auf keinen Fall geschlossen werden ... Dieses Phänomen wird von uns als BUGs bezeichnet. Es gibt aber auch Fälle, in denen ein asynchroner Vorgang kein „Echo“ ausgibt und stillschweigend beendet wird. Niemand weiß davon, und nach dem Aktualisieren der Seite bleibt nicht einmal eine Spur davon übrig.

Axios verfügt über eine Timeout-Behandlung

Die Verwendung von Axios zum Tätigen von Web-API-Aufrufen ist ein gängiger asynchroner Betriebsvorgang. Normalerweise wird unser Code folgendermaßen geschrieben:

versuchen {
    const res = warte auf axios.get(URL, Optionen);
    //TODO, fahren Sie mit den nachfolgenden Aufgaben wie gewohnt fort} catch(err) {
    // TODO: Fehlertoleranz durchführen oder einen Fehler melden}

Dieser Code funktioniert normalerweise gut, bis sich eines Tages ein Benutzer beschwert: Warum erfolgt nach so langer Wartezeit keine Antwort?

Dann erkannte der Entwickler, dass es aufgrund der erhöhten Belastung des Servers schwierig war, sofort auf diese Anfrage zu reagieren. Unter Berücksichtigung der Gefühle des Benutzers wird eine Ladeanimation hinzugefügt:

versuchen {
    zeigeLaden();
    const res = warte auf axios.get(URL, Optionen);
    //TODO normales Geschäft} catch (err) {
    //TODO Fehlertoleranzverarbeitung} finally {
    ausblendenLaden();
}

Eines Tages sagte jedoch ein Benutzer: „Ich habe eine halbe Stunde gewartet, aber es drehte sich einfach immer weiter im Kreis!“ Der Entwickler erkannte also, dass die Anfrage aus irgendeinem Grund hängen geblieben war. In diesem Fall sollte die Anfrage erneut gesendet oder direkt an den Benutzer gemeldet werden – oder es sollte eine Timeout-Prüfung hinzugefügt werden.

Glücklicherweise kann Axios mit Timeouts umgehen. Fügen Sie einfach ein timeout: 3000 in options hinzu, um das Problem zu lösen. Wenn ein Timeout auftritt, können Sie es im catch -Block erkennen und behandeln:

versuchen {...}
fangen (Fehler) {
    wenn (err.isAxiosError && !err.response && err.request
        && err.message.startsWith("Zeitüberschreitung")) {
        // Wenn die Axios-Anfrage falsch ist und es sich bei der Nachricht um eine verzögerte Nachricht handelt, // behandelt TODO das Timeout}
}
Endlich {...}

Axios ist in Ordnung, aber was ist, wenn wir fetch() verwenden?

Umgang mit fetch()-Timeouts

fetch() selbst kann keine Timeouts verarbeiten, daher müssen wir das Timeout ermitteln und AbortController verwenden, um die Anforderungsoperation „Abbrechen“ auszulösen.

Wenn Sie einen fetch() Vorgang abbrechen müssen, holen Sie einfach signal von einem AbortController -Objekt und übergeben Sie das Signalobjekt als Option fetch() . Wahrscheinlich ist es so:

const ac = neuer AbortController();
const { signal } = ac;
holen(URL, {Signal}).dann(res => {
    //TODO Geschäftliches erledigen});
 
// Den Abrufvorgang nach 1 Sekunde abbrechen. setTimeout(() => ac.abort(), 1000);

ac.abort() sendet ein Signal an signal , löst dessen abort aus und setzt dessen Eigenschaft .aborted auf true . fetch() -Verarbeitung verwendet diese Informationen, um die Anforderung abzubrechen.

Das obige Beispiel zeigt, wie die Timeout-Behandlung für fetch() Operationen implementiert wird. Wenn Sie zur Verarbeitung await verwenden, müssen Sie setTimeout(...) vor fetch(...) setzen:

const ac = neuer AbortController();
const { signal } = ac;
setTimeout(() => ac.abort(), 1000);
const res = warte auf fetch(url, { signal }).catch(() => undefiniert);

Um die Verwendung try ... catch ... zur Behandlung von Anforderungsfehlern zu vermeiden, wird nach fetch() ein .catch(...) hinzugefügt, um Fehler zu ignorieren. Wenn ein Fehler auftritt, wird res der Wert undefined zugewiesen. Für die tatsächliche Geschäftsabwicklung ist möglicherweise eine angemessenere catch() Verarbeitung erforderlich, damit res identifizierbare Fehlerinformationen enthalten können.

Wir hätten hier aufhören können, aber das Schreiben eines so langen Codestücks für jeden fetch() -Aufruf wäre mühsam, also kapseln wir es zusammen:

asynchrone Funktion fetchWithTimeout(timeout, resoure, init = {}) {
    const ac = neuer AbortController();
    konstantes Signal = Wechselstromsignal;
    setTimeout(() => ac.abort(), Zeitüberschreitung);
    returniere fetch(Ressource, { ...init, Signal });
}

Ist das ok? Nein, es gibt ein Problem.

Wenn wir im setTimeout(...) des obigen Codes eine Nachricht ausgeben:

setzeTimeout(() => {
    console.log("Es ist eine Zeitüberschreitung");
    ac.abort();
}, Zeitüberschreitung);

Und geben Sie dem Anruf ausreichend Zeit:

fetchWithTimeout(5000, url).then(res => console.log("Erfolg"));

Wir sehen die Ausgabe success und nach 5 Sekunden die Ausgabe It's timeout .

Übrigens: Obwohl wir das Timeout für fetch(...) behandelt haben, haben wir timer nicht beendet, als fetch(...) erfolgreich war. Wie konnte einem aufmerksamen Programmierer ein solcher Fehler unterlaufen? Töte ihn!

asynchrone Funktion fetchWithTimeout(timeout, resoure, init = {}) {
    const ac = neuer AbortController();
    konstantes Signal = Wechselstromsignal;    
    const timer = setTimeout(() => {
        console.log("Es ist eine Zeitüberschreitung");
        return ac.abort();
    }, Zeitüberschreitung);    
    versuchen {
        returniere „warte auf Abruf“ (Ressource, { ...init, Signal });
    Endlich
        Zeitüberschreitung löschen(Timer);
    }
}

Perfekt! Aber das Problem ist noch nicht gelöst.

Bei allem kann es zu einer Zeitüberschreitung kommen

Sowohl Axios als auch Fetch bieten Möglichkeiten, asynchrone Vorgänge zu unterbrechen, aber was ist mit einem normalen Promise, das nicht über abort Abbruchfunktion verfügt?

Zu einem solchen Versprechen kann ich nur sagen: Lasst ihn gehen, lasst ihn es bis ans Ende aller Zeiten tun – ich kann ihn sowieso nicht aufhalten. Aber das Leben muss weitergehen, ich kann nicht länger warten!

In diesem Fall können wir setTimeout() in ein Promise einbinden und dann Promise.race() verwenden, um „kein Warten nach Ablauf der Zeit“ zu implementieren:

Rennen bedeutet Rennen, also ist das Verhalten von Promise.race() leicht zu verstehen, oder?

Funktion warteMitTimeout(Versprechen, Timeout, TimeoutMessage = "Timeout") {
    lass den Timer;
    const timeoutPromise = neues Versprechen((_, ablehnen) => {
        Timer = Timeout festlegen(() => ablehnen(Timeout-Nachricht), Zeitüberschreitung);
    }); 
    returniere Promise.race([timeoutPromise, Versprechen])
        .finally(() => clearTimeout(timer)); // Vergessen Sie nicht, den Timer zu löschen
}

Sie können ein Timeout schreiben, um den Effekt zu simulieren:

(asynchron () => {
    const business = neues Versprechen(auflösen => setTimeout(auflösen, 1000 * 10));
    versuchen {
        warte auf waitWithTimeout(business, 1000);
        console.log("[Erfolg]");
    } fangen (Fehler) {
        console.log("[Fehler]", err); // [Fehler] Zeitüberschreitung
    }
})();

Oben finden Sie ausführliche Informationen zur perfekten Lösung für asynchrone Front-End-Timeout-Operationen in JavaScript. Weitere Informationen zur Lösung asynchroner Front-End-Timeout-Operationen finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Zusammenfassung mehrerer gängiger Verarbeitungsmethoden für asynchrone JavaScript-Operationen
  • Drei Lösungen für das asynchrone Laden von js
  • Eine kurze Erläuterung zum ordnungsgemäßen Umgang mit asynchronen JavaScript-Fehlern
  • Lernen Sie von mir JavaScript, um Ausnahmen bei der asynchronen Programmierung zu lösen
  • Korrekte Handhabung des Vue Axios-Anforderungstimeouts

<<:  So ändern Sie die Standardübermittlungsmethode des Formulars

>>:  Beispielcode zur Implementierung einer Hohlmaskenebene mit CSS

Artikel empfehlen

jQuery implementiert einen Zeitselektor

In diesem Artikelbeispiel wird der spezifische Co...

Windows 10 1903 Fehler 0xc0000135 Lösung [empfohlen]

Windows 10 1903 ist die neueste Version des Windo...

Erfahren Sie in einem Artikel mehr über TypeScript-Datentypen

Inhaltsverzeichnis Grundtypen jeder Typ Arrays Tu...

JavaScript imitiert den Jingdong-Lupeneffekt

In diesem Artikel wird der spezifische Code für J...

So finden und löschen Sie doppelte Datensätze in MySQL

Hallo zusammen, ich bin Tony, ein Lehrer, der nur...

Lösung für den ONLY_FULL_GROUP_BY-Fehler in Mysql5.7 und höher

Während des jüngsten Entwicklungsprozesses handel...

MySQL-Optimierung: Cache-Optimierung (Fortsetzung)

In MySQL gibt es überall Caches. Wenn ich den Que...

Was macht der legendäre VUE-Syntax-Sugar?

Inhaltsverzeichnis 1. Was ist syntaktischer Zucke...

Lernbeispiel für den Nginx Reverse Proxy

Inhaltsverzeichnis 1. Reverse-Proxy-Vorbereitung ...

Detaillierte Erläuterung der kombinierten MySQL-Abfrage

Verwenden von UNION Die meisten SQL-Abfragen best...

Website User Experience Design (UE)

Ich habe gerade einen von JunChen verfassten Beitr...

So stellen Sie MySQL-Master und -Slave in Docker bereit

Bild herunterladen Auswählen eines MySQL-Images D...