Beide Seiten, vorherige Überarbeitung Vorherige Überarbeitung Nächste Überarbeitung | Vorherige Überarbeitung |
ef_informatik:web_app [2021-08-26 07:36] – [Javascript 2: Asynchronous code] hof | ef_informatik:web_app [2021-12-15 13:41] (aktuell) – [Async Functions] hof |
---|
# Javascript | # Javascript |
| |
Die meisten Browser können Javascript (oder ECMAScript) ausführen, um Webseiten dynamisch zu gestalten. Die Syntax ist ähnlich wie C++, C# oder Java, am bestem am Beispiel: | Die meisten Browser können Javascript (oder ECMAScript) ausführen, um Webseiten dynamisch zu gestalten. Die Syntax ist ähnlich wie `C++`, C# oder Java, am bestem am Beispiel: |
| |
<file ecmascript connect_four.js> | <file ecmascript connect_four.js> |
* Hinweis: [[https://developer.mozilla.org/de/docs/Learn/JavaScript/Objects/JSON|JSON Referenz]] | * Hinweis: [[https://developer.mozilla.org/de/docs/Learn/JavaScript/Objects/JSON|JSON Referenz]] |
| |
| ## Musterlösung 2 |
| |
| Wie immer gilt: es gibt unendlich viele korrekte Lösungen. {{ :ef_informatik:ksr_ef_viergewinnt-js_game.zip |}} |
| |
# HTTP | # HTTP |
## Request & Response | ## Request & Response |
{{ :ef_informatik:http_404.jpg?nolink&500|}} | {{ :ef_informatik:http_404.jpg?nolink&500|}} |
Jeder Request verlangt eine bestimmte //Ressource// mit einer bestimmten //Method//. Die //Method// beschreibt die Aktion, typischerweise //GET//, um eine Ressource zu holen, //PUT// um eine Ressource auf dem Server zu verändern. | Jeder Request verlangt eine bestimmte //Ressource// mit einer bestimmten //Method//. Die //Method// beschreibt die Aktion, typischerweise //GET//, um eine Ressource zu holen, //POST// um eine Ressource auf dem Server zu verändern. |
| |
Der Request enthält eine Anzahl zusätzlicher Informationen als //Headers//, insbesondere: | Der Request enthält eine Anzahl zusätzlicher Informationen als //Headers//, insbesondere: |
| |
//Session-Cookies// leben nur, solange das Browser-Fenster offen ist und sind in der Regel unproblematisch. Long-lived cookies können über Jahre installiert bleiben und bei jedem Seitenbesuch erneuert werden. Sie werden zum Teil benützt, um Benutzer über Webseiten hinweg zu verfolgen, und haben zur ganzen Diskussion um Cookies geführt. Um den Benutzer zu identifizieren (zum Beispiel nach erfolgreichem Login) sind sie aber unverzichtbar. | //Session-Cookies// leben nur, solange das Browser-Fenster offen ist und sind in der Regel unproblematisch. Long-lived cookies können über Jahre installiert bleiben und bei jedem Seitenbesuch erneuert werden. Sie werden zum Teil benützt, um Benutzer über Webseiten hinweg zu verfolgen, und haben zur ganzen Diskussion um Cookies geführt. Um den Benutzer zu identifizieren (zum Beispiel nach erfolgreichem Login) sind sie aber unverzichtbar. |
| |
## Javascript Fetch | |
Requests werden im klassischen Web für eine HTML-Ressource und die darin eingebundenen Ressourcen (Stylesheets, Bilder, Javascript) ausgelöst. Sie können aber auch vom Javascript-Code angefordert werden. Dieser Mechanismus ([[https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch|Fetch]]) wird von allen modernen Webapps benutzt, um Inhalte dynamisch nachzuladen. Beispielsweise lädt Google Maps Kartenkacheln nach, wenn der Kartenausschnitt durch Zoomen oder Verschieben verändert wird. | |
| |
Wir werden diese Technik verwenden, um den Spielstand auf dem Server nachzuladen, wenn der andere Spieler gespielt hat. | |
| |
# Web-App mit Flask | # Web-App mit Flask |
Um diese Problem zu lösen, werden wir die App auf einen Web-Server verschieben. | Um diese Problem zu lösen, werden wir die App auf einen Web-Server verschieben. |
* Install Flask mit ''pip3 install flask''. | * Install Flask mit ''pip3 install flask''. |
* Move static files to ''/static''. | * Use the [[https://flask.palletsprojects.com/en/2.0.x/quickstart/|quick start guide]] to help you with the following tasks. |
| * Transfer existing code to web app: |
| * Move static files to ''/static''. |
| * Create ''app.py'' in root folder to create the simple-most web app. |
| <code python app.py> |
| from flask import Flask |
| |
| app = Flask(__name__) |
| |
| @app.route("/") |
| def hello_world(): |
| return "<p>Hello, World!</p>" |
| </code> |
* Turn HTML into a Jinja2 template. | * Turn HTML into a Jinja2 template. |
* Add JSON endpoints | * Create a connect-four game model in python. |
| * Our App will communicate with the Server through JSON endpoints |
| * see an [[https://de.wikipedia.org/wiki/Representational_State_Transfer#Beispiel|example of a REST endpoint]] |
| * we need endpoints for |
| * fetching gamestate |
| * inserting a piece |
| * tricky part: identify the user using [[https://flask.palletsprojects.com/en/2.0.x/quickstart/#sessions|sessions]] |
| * Instead of holding game state on the client side, fetch it from the server. |
| |
| Weitere Ideen: |
| * Long polling für den Game-State |
| * Websockets (nicht direkt unterstützt in Flask, aber in [[https://flask-socketio.readthedocs.io/|Flask-SocketIO]] oder [[https://pgjones.gitlab.io/quart/|Quart]]) |
| |
| # Javascript 2 |
| |
| ## Fetch |
| Requests werden im klassischen Web für eine HTML-Ressource und die darin eingebundenen Ressourcen (Stylesheets, Bilder, Javascript) ausgelöst. Sie können aber auch vom Javascript-Code angefordert werden. Dieser Mechanismus ([[https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch|Fetch]]) wird von allen modernen Webapps benutzt, um Inhalte dynamisch nachzuladen. Beispielsweise lädt Google Maps Kartenkacheln nach, wenn der Kartenausschnitt durch Zoomen oder Verschieben verändert wird. |
| |
| Wir werden diese Technik verwenden, um den Spielstand auf dem Server nachzuladen, wenn der andere Spieler gespielt hat. |
| |
# Javascript 2: Asynchronous code | ## Asynchronous code |
| |
Eine Herausforderung in Javascript (und nicht nur dort) ist der Umgang mit längeren Aufgaben. Reagiert unser Code ja auf einen Event (z.B. einen Klick) und führt dann eine kurze Aufgabe (z.B. den Game-State ändern) aus, ist das kein Problem. Wollen wir eine potentiell längere Aufgabe (insbesondere: Network-Traffic über ''fetch()'', oder eine Animation) ausführen, reklamiert der Browser, weil wir damit den Event-Thread blockieren. Das heisst, dass andere Events nicht richtig ausgeliefert werden können, und die App "eingefroren" ist. | Eine Herausforderung in Javascript (und nicht nur dort) ist der Umgang mit längeren Aufgaben. Reagiert unser Code ja auf einen Event (z.B. einen Klick) und führt dann eine kurze Aufgabe (z.B. den Game-State ändern) aus, ist das kein Problem. Wollen wir eine potentiell längere Aufgabe (insbesondere: Network-Traffic über ''fetch()'', oder eine Animation) ausführen, reklamiert der Browser, weil wir damit den Event-Thread blockieren. Das heisst, dass andere Events nicht richtig ausgeliefert werden können, und die App "eingefroren" ist. |
Wir wollen längere Aufgaben //asynchron// oder //nebenläufig// ausführen können. Modernes Javascript unterstützt die Programmierung von asynchronem Code mit zwei Konstrukten: ''Promises'' und den ''async'' / ''await'' Keywords, die auf ''Promise'' aufbauen. | Wir wollen längere Aufgaben //asynchron// oder //nebenläufig// ausführen können. Modernes Javascript unterstützt die Programmierung von asynchronem Code mit zwei Konstrukten: ''Promises'' und den ''async'' / ''await'' Keywords, die auf ''Promise'' aufbauen. |
| |
## Promise | ### Promise |
| |
Eine ''Promise'' ist ein //Versprechen// für das Resultat einer nebenläufigen Aufgabe. Die Promise ist zu beginn im Zustand ''pending'' und geht von da entweder über zu ''fulfilled'' (die Aufgabe war erfolgreich) oder ''rejected'' (die Aufgabe ist fehlgeschlagen). ''fulfilled'' und ''rejected'' werden zusammen als ''settled'' bezeichnet. | Eine ''Promise'' ist ein //Versprechen// für das Resultat einer nebenläufigen Aufgabe. Die Promise ist zu beginn im Zustand ''pending'' und geht von da entweder über zu ''fulfilled'' (die Aufgabe war erfolgreich) oder ''rejected'' (die Aufgabe ist fehlgeschlagen). ''fulfilled'' und ''rejected'' werden zusammen als ''settled'' bezeichnet. |
</code> | </code> |
| |
## Async Functions | ### Async Functions |
| |
Promises sind grossartig, aber der Code ist etwas gewöhnungsbedürftig. Seit 2017 kann dasselbe eleganter geschrieben werden: | Promises sind grossartig, aber der Code ist etwas gewöhnungsbedürftig. Seit 2017 kann dasselbe eleganter geschrieben werden: |
| |
Merke: | Merke: |
* ''await'' wartet auf das Fulfilment einer Promise. Das bedeutet auch, dass die Methode nicht potentiell lange dauern könnte. | * ''await'' wartet auf das Fulfilment einer Promise. Das bedeutet auch, dass die Methode lange dauern könnte. |
* ''await'' ist deshalb nur in ''async'' Funktionen erlaubt. Async Funktionen geben immer eine Promise zurück, auch wenn das im Code nicht mehr sichtbar ist. | * ''await'' ist deshalb nur in ''async'' Funktionen erlaubt. Async Funktionen geben immer eine Promise zurück, auch wenn das im Code nicht mehr sichtbar ist. |
* Fehler können mit dem gewohnten ''try-catch'' gefangen werden. | * Fehler können mit dem gewohnten ''try-catch'' gefangen werden. |
* [[https://web.dev/promises/|more on Promises]] | * [[https://web.dev/promises/|more on Promises]] |
| |
| {{ :ef_informatik:javascript_async_fetch_promises.png?nolink&600 | }} |