8. August 2014 | | gespeichert unter Coding.

Closures war das bestimmende Thema meines vorherigen Blog-Posts, in dem ich die Vorzüge der Funktion als ein Objekt höherer Ordnung darstellte. Der Quellcode kann dadurch schlanker sein und auch die Kapselung von Klassen geht damit leicht von der Hand. Teil drei, und damit der letzte Teil in meiner Reihe (hier ist Teil 1 und Teil 2) über die JavaScript-Funktion, beschäftigt sich diesmal mit vollmundigen Versprechungen: den Promises (oder auch Futures). Grob zusammengefasst ist ein Promise ein Wrapper, der möglicherweise erst in der Zukunft einen Wert enthält. Aber warum möchte man das und wie funktioniert das?

Aus der Callback-Hölle

JavaScript hat ein recht simples Ausführungsmodel, alles hört auf den Event-Loop, der i.d.R. bis zu 60 mal pro Sekunde ausgeführt wird, und dieser läuft in einem einzigen Thread. Daraus resultiert natürlich, dass nebenläufige oder parallele Prozesse nicht ohne Weiteres möglich sind.1 Das ist auch gar nicht notwendig, denn JavaScript arbeitet im Bezug auf Input/Output nicht-blockierend. Das heißt, dass Event-Handler sowohl den Input als auch die Resultate des Outputs behandeln. Sende ich also eine Ajax-Anfrage mit jQuery, unterbricht die Ausführung nicht und geht erst mit dem Ergebnis der Anfrage weiter, sondern ich muss ein Callback angeben, das die Antwort entgegennimmt. In der Zwischenzeit kann also ganz anderer Code ausgeführt werden, das Ganze läuft also asynchron.

So toll das auch sein mag, hat das einen entscheidenden Nachteil: Sind mehrere aufeinanderfolgende asynchrone Anfragen notwendig, verschachteln sich die Callbacks gerne mal sehr tief. Das mindert die Lesbarkeit des Quelltextes und erhöht durchaus auch Fehler aufgrund der unterschiedlichen Bindungen der einzelnen Closures (siehe nochmals den vorangegangenen Blog-Post). So etwas wird auch als Callback-Hell bezeichnet. Das folgende Code-Listing zeigt das einmal beispielhaft mit der Facebook-API:

So, oder so ähnlich, kann ein Code-Schnipsel für eine Facebook-App aussehen. Leider ist das Konstrukt nicht leicht zu lesen und zusätzlich gibt es mehrere Stellen, die die gleiche Funktionalität fordern. Wie kann man das also verbessern? Eine Lösung ist, eine Sequenzierung einzuführen. Und in diesem Fall kommen Promises ins Spiel. Dazu empfiehlt sich z.B. die q-promises-Bibliothek. Die README sowie das Wiki des Projekts bieten einen sehr guten Einstieg.

Von vollmundigen Versprechungen

Soll eine asynchrone Aktion durchgeführt werden, wird zunächst ein sogenanntes „Deferred Object“ (engl. aufgeschobenes Objekt) erzeugt. Dieses enthält das letztendliche Promise und zwei wesentliche Funktionen: resolve( value ) und reject( reason ). Sobald der Browser die entsprechenden Events sendet, kann die entsprechende Antwort an das „Deferred“ weitergeleitet werden. Bei einer positiven Antwort, wird die resolve-Methode mit der Antwort als Wert aufgerufen, bei einer negativen Antwort wird die reject-Methode mit dem Fehler bzw. einer Fehlerbeschreibung als String aufgerufen. Das Promise-Objekt hat im Wesentlichen die then-Methode (promise.then( onResolved, onRejected, onNotified)). Diese wird mit bis zu drei Callbacks als Argumente aufgerufen und gibt jeweils wiederum immer ein Promise-Objekt zurück. Dadurch lassen sich Aktionen bequem verketten. Der erste Callback (onResolved) soll ein positives Ergebnis des asynchronen Aufrufs entgegennehmen. Der zweite Callback (onRejected) wird bei einem negativen Ergebnis ausgeführt. Der letzte Callback (onNotified) ist der einzige, der mehrfach aufgerufen werden kann. Dadurch kann man z.B. den Fortschritt bei einem Datei-Upload abbilden. Nun aber ein Code-Beispiel:

Die Arbeit mit dem „Deferred Object“ kann man sich dank der q-Bibliothek auch ersparen, da sie jQuery-Ajax-Requests entsprechend behandeln kann. Die ajax-Methode von jQuery gibt ebenfalls Promises zurückgeben, nur sind diese nicht ganz so mächtig. Ein Video beleuchtet den Unterschied genauer, weshalb ich hier nicht näher darauf eingehen werde. Wie kann man also unsere request-Funktion kürzer schreiben:

Das ist doch gar nicht schlecht, oder? Wir überlassen es der q-Bibliothek, wie sie den Ajax-Request auswertet und kümmern uns nur noch um die Handler-Funktionen. Wichtig dabei: Die Rückgabewerte der Handler werden unverändert an den nächsten Handler weitergegeben, sofern sie selbst kein Promise sind. Gehen wir nun den entscheidenden Schritt weiter und schauen uns an, wie wir unseren Facebook-Code aufräumen und übersichtlicher gestalten können. Zunächst brauchen wir sogenannte Wrapper-Funktionen, die die Aufrufe der Facebook-Methoden in Promises umwandeln:

Das mag zwar deutlich mehr Code sein als noch im Eingangs-Beispiel, der Vorteil ist nun aber, dass die Angabe eines Callbacks nicht mehr benötigt wird und jede Funktion sofort ein Promise zurückgibt. Nun soll natürlich nicht die Implementierung der Sequenzierung unserer Facebook-Anfrage fehlen:

Das sieht doch gleich viel aufgeräumter aus, oder? Wir haben einen klaren und leicht lesbaren Kontrollfluss und doppelte Aufrufe werden vermieden. Der Quelltext ist somit leichter wartbar und ein neuer Entwickler kann sich recht schnell zurechtfinden.

Zusätzlich sind einige Schmankerl der q-promises enthalten. So weiß die Bibliothek, wie es mit den Rückgabe-Werten umgehen soll. Im ersten then-Handler sind im if-else-Statement drei entsprechende Fälle zu sehen. Fall eins gibt normal das Argument zurück, die q-Bibliothek reicht dieses an den nächsten Handler weiter. Fall zwei wirft einen Fehler, dieser wird von der q-Bibliothek abgefangen und an den nächstmöglichen Fehler-Handler weitergeleitet. In diesem Fall würden sämtliche anderen Handler ignoriert und komplett zur onError-Funktion gesprungen. Diese wird dann mit dem geworfenen Fehler als Argument aufgerufen. Der Fehler kann abgefangen werden und eine völlig andere Sequenz von Aktionen zur Folge haben. Der dritte Fall gibt ein neues Promise-Objekt zurück, dessen Sequenz die q-Bibliothek erst abarbeitet, bevor sie die übergeordnete Sequenz weiterverfolgt.

Was wir hier bisher sehen, sind nur ein paar Aspekte von Promises. Sie sind noch weitaus vielfältiger und ein Blick in die Dokumentation lohnt sich. Sie sind auch ein weiteres Beispiel für die Vielseitigkeit der JavaScript-Funktion und eine Brücke hin zur funktionalen Programmierung, in der Konstrukte wie Promises ein wesentlicher Bestandteil sind.

Persönlich habe ich Promises schon lange im Einsatz. Nutzt ihr sie ebenfalls? Was sind eure Erfahrungen? Oder habt ihr vielleicht andere oder gar bessere Ansätze? Berichtet mir von euren Erfahrungen.

Damit schließe ich meine Artikel-Reihe zur JavaScript-Funktion. Ich hoffe, es hat euch gefallen und ihr konntet neue Inspiration für eure tägliche Arbeit daraus ziehen. Bei Kritik und Anregungen schreibt einfach einen Kommentar. Derweil werde ich meine magische Kugel nach den nächsten Themen befragen. Bis dahin: Stay tuned!


  1. Die sogenannente Web-Worker sind eine Möglichkeit, Code außerhalb des Event-Loops ausführen zu lassen

13. Juni 2014 | | gespeichert unter User Experience.

Schon Manta-Fahrer wussten: Rallye-Streifen lassen die Karre schneller wirken. Dank der aufgemotzten Optik sieht die verbeulte Schüssel aus wie ein Rennwagen, auch wenn bei 140 km/h der Motor versagt.

Dieses Manta-Mantra ist anscheinend bei Facebook angekommen…. Das soziale Netzwerk kämpfte mit dem Problem, dass seine iOS-App recht lange Ladezeiten ausfüllte. Alle technische Optimierung half nichts – Nutzer hatten das Gefühl, die App wäre zu langsam.

Jetzt übernahmen die Facebook-Designer das Steuer und schraubten – getreu dem Manta-Mantra – an der Ladeanzeige. Wenn es nicht schneller wird, dann sorge dafür, dass es schneller aussieht.

Ich habe hier zwei Design-Versionen nachgestellt. Bei welcher hielten Testnutzer die App für schneller?

vergleich_facebook_loader_ios

Auflösung: Die Facebook-eigene Animation (links) schnitt im A/B-Vergleich schlechter ab. Testnutzer machten die Facebook-App für die lange Ladezeit verantwortlich. Bei Anzeige des iOS-Loaders hingegen glaubten die Nutzer, die geringe Leistung des iPhones würde die App ausbremsen.

Nun stand Branding (die sonst überall von Facebook genutzte Animation) gegen User-Experience. In diesem Fall entschied man sich augenscheinlich für die User-Experience, denn sowohl unter Android als auch unter iOS wird die jeweilige Animation des Betriebssystems verwendet.

Es drängt sich die Frage auf, ob das schon ein sog. „Dark-Pattern“ der UX ist? Also eine Methode bei der zu Ungunsten der Nutzer etwas bewusst versteckt oder vorgegaukelt wird. Aber dieses Thema hebe ich mir für einen anderen Beitrag auf. Bis dahin weiß ich, dass wir bei der nächsten App eher auf die individualisierte Lade-Animation verzichten und die Arbeitszeit für Besseres einsetzen.

Bildquelle Titelbild: Viktor Hanacek, Picjumbo / Tobias Loy
Quelle zum Thema: Talk von Vitaly Friedman auf der Typo 2014 Berlin
Urheberrechte: Facebook, das Facebook-Logo, sind eingetragene Warenzeichen von Facebook Inc.. iOS, iPhone sind eingetragene Warenzeichen von Apple Inc.

17. Mai 2012 | | gespeichert unter Programmierung, Projekte.

„Fußball ist unser Leben, denn König Fußball regiert die Welt. Wir kämpfen und geben Alles, bis dann ein Tor nach dem andern fällt.“… 

Das ansteckende Fußballfieber ist wieder ausgebrochen! Auf den Balkons, Kneipen und in den Wohnzimmern ist mitfiebern und lautstarkes Anfeuern vor den Fernsehgeräten angesagt. Das Finale der UEFA Champions League findet am 19. Mai 2012 auf deutschem Boden in der Fußball Arena München statt.
Bereits seit 20 Jahren ist Ford Sponsor dieses populären Wettbewerbs. Zu diesem Anlass hält Ford Deutschland für seine Facebook-Fans eine kultverdächtige Fußball-App auf seiner Fanpage bereit, die sowohl Freunde als auch Dulder des Fußballs gleichermaßen begeistern wird.

fussball_04_ext

Fußballer-Yourself

Die App „Ford Fußball Champion“verwandelt dich in einen richtigen Kultkicker. Du kannst auf Facebook deine eigene virtuelle Fußballmannschaft gründen oder einer bereits angelegten Mannschaft beitreten. Zuvor hast du die Möglichkeit, dein Foto hochzuladen und dieses im Stile der Fußball-Ikonen vergangener Dekaden zu gestalten. Egal ob Dauerweller, Oberlippenbard oder Vokuhila: erlaubt ist, was gefällt! Du kannst aus insgesamt 80 Kombinationsmöglichkeiten verschiedene Frisuren und Trikots wählen, die den Spielern der verschiedenen Jahrzehnte nachempfunden sind.

Zu einer Mannschaft gehören natürlich immer 11 Spieler, also dürfen im Anschluss Freunde zur eigenen virtuellen Fußballmannschaft eingeladen werden. Ist die „Elf“ komplett, kann der Kapitän ein individuell gestaltetes Mannschafts-Logo hochladen oder sich eines aus drei vorhandenen auswählen.

fußball_06
Damit sich die Arbeit auch langfristig lohnt, kann sich jeder Spieler – nachdem die Mannschaft vom Kapitän als „abgeschlossen“ eingestuft wurde – einen kompletten Satz Sammelaufkleber mit 11 Profilbildern, dem Team-Namen und Team-Logo kostenlos nachhause schicken lassen. Das passende Poster zu den Aufklebern der „Kultkicker“-Mannschaft gibt es beim teilnehmenden Ford Händler vor Ort. Sowohl Aufkleber wie auch Poster gibt es nur, solange der Vorrat reicht.

fußball_07

Auch wir haben mitgemacht und die Mannschaft „FC PLUSPOL“ gegründet. Unser Mannschafts-Poster hängt im Flur und erinnert uns an die zweite Strophe:„Ja, Einer für Alle, Alle für Einen. Wir halten fest zusammen, und ist der Sieg dann unser, sind Freud‘ und Ehr für uns alle bestellt.„…

fussball_01_ext

Die technische Umsetzung

Im Rahmen des Projekts entwickelte PLUSPOL interactive im Auftrag von Wunderman Köln für die Ford-Werke GmbH eine Flash-Anwendung mit PHP-Backend. Flash und PHP kommunizieren dabei über eine eigene JSON-RPC-Client-Server-Schnittstelle mit Sessionverwaltung und Authentifizierung.
Das Backend verarbeitet die vom Client übermittelten Inhalte und erzeugt serverseitig druckfähige Bilddaten. Außerdem wurde im Backend eine automatische Fehlerberichterstattung bei Auftreten serverseitiger Fehler implementiert. Im Flash-Client wurde ein Bildkonfigurator mit verschiedenen Manipulationswerkzeugen realisiert. Ein administratives Back-Office ermöglicht die Freigabe der durch die Nutzer generierten Bilder. Aufgrund dieses asynchronen Freigabeprozesses durch die Administratoren galt es, komplizierte Abläufe der Teamlogik zu verarbeiten.