Seminar Höllische Programmiersprachen im Wintersemester 2014/15 Callbacks und Event Handlers Vanessa Robl Technische Universität München 18.12.2014 1 Zusammenfassung Callbacks und Event Handlers sind Funktionalitäten, die beim Programmieren in einigen verschiedenen Sprachen genutzt werden. In dieser Arbeit wird vorgestellt, in welchen Bereichen sie eingesetzt werden und welchen Nutzen sie für den Programmierer haben. Mit Codebeispielen aus den Programmiersprachen HTML, JavaScript und Visual C# wird näher darauf eingegangen, wie die Anwendung in der Praxis aussieht. Es werden außerdem möglicherweise auftretende Probleme und zugehörige Lösungsansätze und Alternativen diskutiert. Des Weiteren werden Vorund Nachteile abgewogen, die abschließend mit einer kurzen Abschätzung der zukünftigen Entwicklung verknüpft werden. 2 Inhaltsverzeichnis 1 Definitionen und Begriffseinführung 1.1 Was ist ein Callback . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Was ist ein Event Handler . . . . . . . . . . . . . . . . . . . . . . 4 4 4 2 Verwendung in Programmiersprachen 2.1 Anwendungsgebiete . . . . . . . . . . . . . . . . . . 2.1.1 Event-Handler als Bindeglied zwischen User 2.1.2 Gemeinsame Verwendung von Callbacks Handlers . . . . . . . . . . . . . . . . . . . . 2.1.3 Weitere Anwendungsgebiete für Callbacks . 2.2 Probleme bei der Verwendung . . . . . . . . . . . . 2.3 Lösungsansätze für auftretende Probleme . . . . . 5 5 5 3 Pro und Contra der Anwendung 3 . . . . . . . . und Skript . und Event. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 8 10 11 13 1 Definitionen und Begriffseinführung Diese Arbeit thematisiert die Verwendung von Callbacks und Event Handlers. Es werden Anwendungsgebiete sowie möglicherweise auftretende Probleme und passende Lösungsansätze zu diesen Problemen aufgezeigt und diskutiert. Im Folgenden werden zuerst alle Begrifflichkeiten definiert und Codebeispiele gezeigt. 1.1 Was ist ein Callback Eine Callback-Funktion ist eine Funkion (mit oder ohne Rückgabewert), die als Parameter für eine Standardfunktion verwendet oder als Objekt gespeichert werden kann. Sind die richtigen Bedingungen erfüllt, so kann die Funktion aufgerufen und ausgeführt werden. Allgemein gilt, dass Callback-Funktionen nicht statisch sind, sondern erst zur Laufzeit des Programms eingebunden werden. Einsatzgebiet für Callbacks sind zum Beispiel Asynchrone Programmierung und GUI-Events. Übergibt man einer Methode die Callback-Funktion als Argument, so wird sie zu einem bestimmten Zeitpunkt, den der Programmierer selbst festlegen kann, aufgerufen. Wird sie sofort ausgeführt, nennt man das einen synchronen Callback, ansonsten einen asynchronen. Für die GUI-Programmierung sind Callbacks deshalb gut geeignet, weil sie als Rückgabewert eine Funktion haben. So können mit ihnen auch bei Funktionsreferenzen Parameter übergeben werden, was bei Standardfunktionen nicht möglich ist. Callbacks sind insgesamt ein ideales Strukturmittel fr ereignisorientierte Programmierung. Ein Beispiel dafür, wie das Grundgerüst einer Callback-Funktion in JavaScript aussehen kann, ist Folgendes: 1 3 5 function callMe ( ) { return function () { // do s o m e t h i n g }; } Eine Callback-Funktion ist letztendlich also ein Verfahren, das beim Eintreten eines Ereignisses gezielt eine bestimmte Funktion aufruft [13]. 1.2 Was ist ein Event Handler Event-Handler sind Mechanismen, die auf bestimmte Ereignisse (z.B. einen Mausklick) reagieren und die die Callback-Funktion, die diesen Ereignissen zugewiesen wurden, aufrufen. Somit ist die Implementierung von der Verwendung getrennt. Sie haben den Vorteil, dass mit ihrer Anwendung ein Programm nicht mehr in einer Schleife auf ein Ereignis warten muss, sondern erst bei dessen Auftreten aktiv wird. Deswegen werden Event-Handler bei der ereignisorientierten Programmierung verwendet. Jeder Event-Handler steht 4 dabei für ein bestimmtes Anwendungsereignis [12]. Der Name eines Handlers beginnt in vielen Sprachen mit “on”. In JavaScript sieht die zugehörige Syntax (hier im Beispiel der Handler joinList) wie folgt aus: 1 3 var j o i n L i s t = f u n c t i o n ( ) { a l e r t ( ” H i e r s t e h e n d i e Anweisungen f ü r d i e Funktion ” ) ; } Soll nun dieser Handler an ein direktes Ereignis (hier ein Klick auf einen Button) geknüpft werden, so schreibt man: 1 document . getElementById ( ” submit \ b u t t o n ” ) . o n c l i c k = j o i n L i s t ; Bevor solche Verknüpfungen stattfinden können, müssen aber erst das Objekt und das Ereignis, das den Event-Handler triggert, spezifiziert werden. Event-Handler sind primär das Bindeglied zwischen der graphischen Oberfläche und dem Code [10]. 2 Verwendung in Programmiersprachen Callbacks und Event Handlers sind vor allem in GUI-Programmen nützliche Elemente. Im Folgenden werden direkte Anwendungsbeispiele in den Sprachen HTML und JavaScript sowie Visual C# betrachtet. Außerdem kommen auftretende Probleme, wie zum Beispiel die “Callback-Hell”, zur Sprache und mögliche Lösungsansätze werden diskutiert. 2.1 2.1.1 Anwendungsgebiete Event-Handler als Bindeglied zwischen User und Skript Event-Handler werden bei der Programmierung mit visuellen Komponenten benutzt, bei denen eine Art von Interaktion zwischen User und Code vonnöten ist, am häufigsten für Webseiten. Oft werden sie als Bindeglied zwischen HTML und JavaScript benutzt und dabei meist in Form von Attributen in HTML-Tags notiert, über die sich dann JavaScript aktivieren lässt. Ein Tag ist ein Codeelement, das in spitze Klammern eingeschlossen ist. In HTML wären solche Elemente zum Beispiel <link >, <script >, <section> oder <h1> [10]. Betrachten wir das HTML-Attribut onClick in folgendem Beispielcode: 5 1 3 5 <INPUT TYPE = ” checkbox ” NAME = ” o p t s ” VALUE = ” g r o s s −k l e i n ” o n C l i c k = ” g r o s s −k l e i n=t h i s . c h e c k e d ; ” > Der String-Wert in onClick kann eine oder mehrere JavaScript-Anweisungen beinhalten. Sind es mehrere Anweisungen, so müssen diese durch ein Semikolon getrennt werden. Tritt nun dieses Ereignis, das onClick zugeordnet wurde, ein (hier ein Mausklick), wird die Anweisung, die innerhalb des Strings steht, ausgeführt. Damit JavaScript und HTML möglichst voneinander getrennt bleiben und der Code übersichtlich ist, wird – wenn mehrere Anweisungen vorhanden sind – üblicherweise der Rumpf des Event-Handlers als Funktion definiert und diese Funktion dann über den Event-Handler aufgerufen. Dabei wird die Funktion zwischen den Tags <SCRIPT > und </SCRIPT> definiert. Das Attribut onClick wird zum Beispiel für Buttons, Checkboxen und RadioButtons verwendet. Bei Textelementen können andere Event-Handler benutzt werden, wie beispielsweise onchange(), onfocus() oder onblur(). In Tabelle 1 werden ein paar von JavaScript unterstützte Handler zusammen mit ihrem jeweiligen Einsatzgebiet aufgeführt [6]. Tabelle 1: Event-Handler in JavaScript Event-Handler onBlur onClick onChange onFocus onLoad onMouseMove Aufgerufen, wenn... wenn ein Objekt den Tastaturfokus verliert wenn der Benutzer auf ein Objekt klickt wenn sich der Wert eines Objektes ändert wenn ein Objekt den Tastaturfokus erhält wenn ein Objekt geladen worden ist wenn der Benutzer die Maus bewegt Unterstützt von Element, Window, Layer Document, Button, Checkbox, Link, Radio, Reset, Submit Element, FileUpload, Passwort, Select, Text, Textarea Element, Window, Layer Document, Image, Window, Layer keinem Objekt; wird nur aufgerufen nach Anforderung durch captureEvents() Bei der Implementierung ist darauf zu achten, dass JavaScript im Unterschied zu HTML Groß- und Kleinschreibung unterscheidet und dass Event-HandlerEigenschaften in JavaScript komplett klein geschrieben werden. Man kann Event-Handler selbst definieren oder verändern, indem man ihnen bestimmte Funktionen zuweist [6]. 6 2.1.2 Gemeinsame Verwendung von Callbacks und Event-Handlers Oft werden im Zusammenhang mit Event-Handlers Callbacks verwendet, sodass die Struktur und die Funktionalität nicht vermischt werden. Um das gewünschte Ergebnis zu liefern, muss dem Event-Handler eine Funktionsreferenz übergeben werden, also ein Zeiger auf eine Funktion. Einer solchen Referenz können jedoch keine Parameter mitgegeben werden. Dieses Problem kann man beheben, indem man dem Handler einen Callback zuweist und ihm so eine Methode übergibt, die eine Funktion als Rückgabewert hat. Dadurch hat man nun die Möglichkeit, mit Parametern zu arbeiten, wobei in manchen Event-Modellen (z.B. Mozilla oder Netscape) der erste Parameter standardmäßig für das Event-Objekt reserviert ist. Es ist nicht relevant, ob die Callback-Funktion anonym oder benannt ist. Ein Beispiel für solch ein Konstrukt in JavaScript ist folgender Code: 2 4 6 8 10 12 14 window . o n l o a d = f u n c t i o n ( ) { document . getElementById ( ” i n f o ” ) . innerHTML = ” ” ; document . getElementsByTagName ( ” bu tt on ” ) [ 0 ] . o n c l i c k = k l i c k ; document . getElementsByTagName ( ” bu tt on ” ) [ 1 ] . o n c l i c k = f u n c t i o n ( ) { klick (1) ; }; document . getElementsByTagName ( ” bu tt on ” ) [ 2 ] . o n c l i c k = f u n c t i o n ( e v t ) { document . getElementById ( ” i n f o ” ) . innerHTML = e v t . pageX + ” , ” + e v t . pageY ; }; }; function klick (a) { document . getElementById ( ” i n f o ” ) . innerHTML = a ; }; Obiges Beispiel ist ein Codeausschnitt. In den Zeilen 3, 4 und 7 wird, wie es der Standard ist, jedem Event-Handler eine Funktionsreferenz zugewiesen. Im Beispiel handelt es sich beim auslösenden Event um einen Mausklick auf einen von drei Buttons. Drückt man den ersten Button, so wird die dritte Zeile aufgerufen. Zeile 3 ist eine Referenz, die kein Callback ist. Da man dieser Funktionsreferenz aber keine Parameter mitgeben kann, wird nur das auslösende Objekt als Ausgabe zurückgegeben, in diesem Fall [object MouseEvent]. Wie man dieses Problem beheben kann, zeigt Zeile 4. Statt dem Event-Handler die Funktion klick direkt zuzuweisen, benutzt man eine Callback-Funktion, die klick als Rückgabewert hat und diese Funktion (diesmal mit Parameter) aufruft. Nun wird beim Drücken des zweiten Buttons der Wert zurückgegeben, dem man klick im Funktionskörper der Callback-Funktion mitgegeben hat. Zeile 7 zeigt, dass man einem Callback auch direkt einen Parameter (hier evt genannt) übergeben kann. Wie bereits erwähnt handelt es sich dabei um das Event-Objekt, also das [object MouseEvent]. Mit den Aufrufen evt.pageX und evt.pageY erhält man die Koordinatenwerte des Mauszeigers jeweils an dem 7 Punkt, an dem sich die Maus beim Klick auf den dritten Button befindet [13]. Wie das Zusammenspiel zwischen HTML und JavaScript beim Einsatz von Event-Handlers aussehen kann, wird in folgendem Beispiel aufgezeigt: HTML: 2 <h1>B i t t e t r e t e n S i e u n s e r e r E−Mail−L i s t e b e i</ h1> < l a b e l f o r=” e m a i l a d d r e s s ”>E−Mail A d r e s s e :</ l a b e l> <i n p u t t y p e=” t e x t ” i d=” e m a i l a d d r e s s ” name=” e m a i l a d r e s s ”><br> 4 6 < l a b e l>&nbsp</ l a b e l> <i n p u t t y p e=” b ut to n ” i d=” j o i n l i s t ” v a l u e=” Tr ete n S i e b e i ! ”><br> JavaScript: 2 4 6 8 10 12 14 16 // Die $ Funktion var $ = f u n c t i o n ( id ) { r e t u r n document . getElementById ( i d ) ; } // Der Event−Handler f ü r den K l i c k a u f e i n e n Button var j o i n L i s t = f u n c t i o n ( ) { a l e r t ( ” Die j o i n t L i s t −Funktion l ä u f t g e r a d e . ” ) ; } // Der Event−Handler f ü r das onchange−E r e i g n i s d e r Textbox v a r changeValue = f u n c t i o n ( ) { a l e r t ( ” Die changeValue−Funktion l ä u f t g e r a d e . ” ) ; } // Der Event−Handler f ü r das onload−E r e i g n i s , das z w e i Event−Handler anh ä ngt window . o n l o a d = f u n c t i o n ( ) { $ ( ” j o i n l i s t ” ) . o n c l i c k = j o i n L i s t ; //hä ngt 1 . Event−Handler an $ ( ” e m a i l a d d r e s s ” ) . o n c l i c k = changeValue ; //hä ngt 2 . Event− Handler an } Dieser Code erzeugt eine Textbox, einen Button und zwei Beschriftungen. Es soll eine Nachricht angezeigt werden, wenn der Button geklickt wird oder wenn der Benutzer den Wert in der Textbox ändert. In der letzten Methode wird window.unload verwendet. Das heißt, diese Funktion wird erst ausgeführt, nachdem die Seite komplett geladen und das DOM (Document Object Model) erstellt wurde. DOM ist eine definierte Norm. Es ist keine eigene Programmiersprache und ist nicht auf HTML beschränkt. Durch DOM werden Objekte, Eigenschaften und Methoden festgelegt, die eine Programmiersprache umsetzen sollte und die auf Dokumente anwendbar sind, die auf XML basieren [10]. 2.1.3 Weitere Anwendungsgebiete für Callbacks Ein weiteres Anwendungsgebiet für Callbacks in JavaScript sind TimeoutMethoden. Sie können zum Beispiel wie folgt benutzt werden: 8 1 3 var thePlotThickens = f u n c t i o n ( ) { c o n s o l e . l o g ( ’ 500ms sp ä t e r . . . ’ ) ; }; setTimeout ( thePlotThickens , 500) ; Die Funktion thePlotThickens wird hier nicht sofort ausgeführt, sondern erst später, wenn sie von setTimeOut aufgerufen wird [11]. Nicht zuletzt wird einem Programmierer durch Callbacks außerdem die Möglichkeit geboten, asynchron zu arbeiten. Das heißt, während auf eine Funktion gewartet wird, können zur selben Zeit andere Dinge ausgeführt werden, was eine erhebliche Zeitersparnis zur Folge hat. Statt zum Beispiel mit einer while-Schleife oder mit einem Timer zu festgelegten Zeitpunkten den aktuellen Status abzurufen, kann mit einer Callback-Methode die asynchrone Operation ohne Unterbrechung durch den aufrufenden Thread (Polling) ausgeführt werden. Im Folgenden betrachten wir ein Beispiel aus Visual C#, bei dem es sich um die Implementierung eines Einlesevorgangs handelt, wobei durch die Callback-Routine die graphische Oberfläche während des Einlesens einer Datei bedienbar bleibt [4]: 2 4 6 ... p u b l i c p a r t i a l c l a s s Form1 : Form { // Routine , d i e a u f S t e u e r e l e m e n t e z u g r e i f e n kann : p u b l i c v o i d FormRefresh ( myState o b j ) { l i s t B o x 1 . I t e m s . Add( ” C al lB ack : −−− F e r t i g −−−” ) ; l i s t B o x 1 . I t e m s . Add( ” C al lB ack : G e l e s e n : ” + o b j . B y t e s G e l e s e n . t o S t r i n g ( ) + ” Bytes ” ) ; } 8 10 12 14 16 // D e l e g a t e f ü r den A u f r u f d e r Invoke−Methode : p r i v a t e d e l e g a t e v o i d F o r m R e f r e s h D e l e g a t e ( myState o b j ) ; // Die H i l f s k l a s s e : p u b l i c c l a s s myState { p u b l i c b y t e [ ] Bytes ; public FileStream f s ; public i n t BytesGelesen ; } 18 20 22 24 26 // Die C a l l b a c k −Routine : public void Fertig ( IAsyncResult asyncResult ) { // H i l f s o b j e k t e t y p i s i e r e n und verwenden : myState s t a t e = ( myState ) a s y n c R e s u l t . A s y n c S t a t e ; s t a t e . B y t e s G e l e s e n = s t a t e . f s . EndRead ( a s y n c R e s u l t ) ; //Üb e r g a b e von D e l e g a t e und H i l f s o b j e k t : t h i s . I n v o k e ( new F o r m R e f r e s h D e l e g a t e ( FormRefresh ) , s t a t e ) ; s t a t e . f s . Close () ; } 28 // Das S t a r t e n d e s a s y n c h r o n e n D a t e n z u g r i f f s : 9 30 32 34 36 38 40 p r i v a t e v o i d Button1 \ C l i c k ( o b j e c t s e n d e r , EventArgs e ) { l i s t B o x 1 . I t e m s . Add( ” S t a r t e L e s e v o r g a n g . . . ” ) ; // H i e r e r z e u g e n w i r u n s e r H i l f s o b j e k t : myState s t a t e o b j = new myState ( ) ; // Die e r f o r d e r l i c h e n Daten s p e i c h e r n : s t a t e o b j . f s = new F i l e t r e a m ( ‘ ‘ 0 1 . j p g ’ ’ , FileMode . Open , F i l e A c c e s s . Read , F i l e S h a r e . Read , 1 0 0 0 , t r u e ) ; s t a t e o b j . Bytes = new b y t e [ s t a t e o b j . f s . Length ] ; // Asynchronen A u f r u f mit H i l f s o b j e k t a l s Parameter s t a r t e n : IASyncResult r e s = s t a t e o b j . f s . BeginRead ( s t a t e o b j . Bytes , 0 , ( i n t ) s t a t e o b j . f s . Length , F e r t i g , s t a t e o b j ) ; } } ... Zur Programmierung kann man auf Optimierungen wie zum Beispiel die Entwicklungsumgebungen Node.js, Konzepte zur asynchronen Datenübertragung wie Ajax (Asynchronous JavaScript and XML) oder auf freie Bibliotheken wie jQuery zurückgreifen. Hierbei ist anzumerken, dass jQuery unter anderem über Ajax-Support verfügt, Funktionen zur DOM-Manipulation und -Navigation bereitstellt sowie ein erweitertes Event-System bietet. 2.2 Probleme bei der Verwendung Werden zu viele Callbacks eingesetzt und hintereinander geschaltet, so wird der Code schnell unübersichtlich und schwer nachvollziehbar. Teilweise muss der komplette Code durchgelesen und verstanden werden, um die Aufgabe einer einzelnen Funktion herauszufinden. In manchen Fällen geht es sogar so weit, dass es kaum mehr möglich ist, weiteren Code hinzuzufügen, ohne Fehler bei anderen, eventuell auf den ersten Blick unbeteiligten Funktionen hervorzurufen. Dieses Problem wird als “Callback-Hell” bezeichnet. Als Beispiel kann folgendes Datei-Auslese-Proramm (JavaScript) verwendet werden: 1 var f s = r e q u i r e ( ” f s ” ) ; var fileName = ” f oo . tx t ” ; 3 5 7 9 11 13 15 17 f s . e x i s t s ( fileName , f u n c t i o n ( e x i s t s ) { if ( exists ) { f s . s t a t ( fileName , f u n c t i o n ( e r r o r , s t a t s ) { i f ( error ) { throw e r r o r ; } i f ( stats . isFile () ) { f s . r e a d F i l e ( f i l e N a m e , ” u t f 8 ” , f u n c t i o n ( e r r o r , data ) { i f ( error ) { throw e r r o r ; } c o n s o l e . l o g ( data ) ; 10 }) ; } }) ; 19 21 } }) ; Hier sieht man bereits ansatzweise, inwieweit Callbacks in der Anwendung zu einem großen Problem werden können. Obwohl es nur die drei Funktionen fs.exists(), fs.stat() und readFile() sind, die hier als Callbacks aufgerufen werden, wirkt der Code bereits jetzt unübersichtlich und unstrukturiert. Kommt nun noch komplexerer Code mit weiteren Callback-Funktionen hinzu, so verliert man in kürzester Zeit den Überblick [9]. Bei Event-Handlers gibt es das allgemeine Problem, dass der mit dem Handler verknüpfte Code nur ausgeführt werden kann, wenn das visuelle Element im Browser schon vollständig geladen und die zugehörige Funktion definiert wurde. Ansonsten wird eine Interaktion des Benutzers mit dem bedienelement nicht erkannt und die Callback-Funktion, die an den Event-Handler geknüpft ist, kann nicht aufgerufen werden. Außerdem ist noch nicht standardisiert festgelegt, welcher Event-Handler in welchem Tag vorkommen darf. Das hat zur Folge, dass in verschiedenen Browsern die Handler unterschiedlich interpretiert werden [6]. Als Beispiel ist der JavaScript-Handler onAbort anzuführen. Er wird für den Fall verwendet, dass ein Benutzer die Anwendung im Browser schließt, bevor alle Grafiken vollständig geladen wurden. Der Code dazu könnte beispielsweise so aussehen: 2 4 <html><head>< t i t l e>Test</ t i t l e> </ head><body> <img s r c=” o n a b o r t . j p g ” width=” 400 ” h e i g h t=” 600 ” a l t=” G r a f i k ” o n a b o r t=” a l e r t ( ’ Schade , d a s s S i e das B i l d n i c h t s e h e n w o l l e n . ’ ) ”> </ body></ html> Dieser Event-Handler kann nur in Microsoft Internet Explorer verwendet werden. Andere Browser wie Opera, Mozilla Firefox oder Safari unterstützen ihn nicht, weil onAbort nicht zum HTML-Standard gehört. 2.3 Lösungsansätze für auftretende Probleme Den im vorherigen Kapitel genannten Problemen mit Event-Handlers kann man begegnen, indem man alle benötigten Funktionen bereits im <HEAD> des HTML-Dokuments definiert oder aber indem man onload() benutzt. onload() ist ein Event-Handler, der erst ausgeführt wird, wenn das Dokument vollständig geladen wurde. Dann erst wird der Zugriff auf die restlichen Event-Handler gestattet [6]. 11 Um die Callback-Hell zu vermeiden, können ebenfalls ein paar einfache, präventive Maßnahmen ergriffen werden. Diese bestehen beispielsweise darin, den Code ausreichend zu kommentieren, nicht zu viele Callbacks hintereinander zu schalten und kurze Namen für die Callback-Funktionen zu wählen. Das reicht aber in vielen Fällen nicht aus, denn oft hat man bei sehr umfangreichem und komplexem Code keine andere Wahl und die Konstrukte kaskadieren. Deshalb sollte über den Einsatz von Functional Reactive Programming (FRP) nachgedacht werden. FRP ist ein Framework zur Beschreibung von zeitabhängigen Relationen. Es lässt asynchrone Funktionsaufrufe ohne die Verwendung von Callbacks zu und ist so konzipiert, dass dabei die Syntax und Semantik des Codes lesbar und leicht nachvollziehbar bleibt [7]. Zur Programmierung stehen Stream-Funktionen wie map, filter, scan, oder merge zur Verfügung, durch die man auf Kontrollflusselemente wie if, for oder while verzichten kann. Statt einer Sequenz von hintereinander geschalteten Anweisungen, die nacheinander abgearbeitet werden, werden dem Computer nur die Beziehungen zwischen Streams mitgeteilt. Das alles führt dazu, dass Konstrukte wie Callbacks, die letztendlich in Problemen wie der Callback-Hell resultieren könnten, vollkommen überflüssig werden. Die Implementierung erfolgt durch Programmiersprachen wie zum Beispiel Haskell oder Elm. Insbesondere Elm ist auf die Kompilierung von JavaScript, HTML und CSS (Cascading Style Sheet) sowie auf die Vereinfachung von GUIImplementierungen ausgelegt. Durch zeitabhängige Variablen, die Input oder Output repräsentieren können, wird dem Programmierer die Möglichkeit gegeben, komplexe Zusammenhänge reaktiv und deklarativ zu implementieren [3]. In den folgenden zwei Codebeispielen wird der Unterschied zwischen JavaScriptCallbacks und Elm klarer. In ihnen geht es jeweils um den Zugriff auf die aktuellen Position der Maus: JavaScript: 2 4 $ ( document ) . r e a d y ( f u n c t i o n ( ) { var p o s i t i o n = { ’ x ’ : 0 , ’ y ’ : 0 } ; $ ( document ) . bind ( ’ mousemove ’ , f u n c t i o n ( e v e t ) { p o s i t i o n = { ’ x ’ : e v e n t . pageX , ’ y ’ : e v e n t . pageY } ; }) ; 6 8 10 setInterval ( function () { // custom p o s i t i o n code } , seconds ∗ 1000) ; }) ; 12 Elm: im po rt Mouse 2 main = l i f t asText Mouse . p o s i t i o n Hier sieht man den Unterschied zwischen den Codefragmenten deutlich. Für die gleiche Aktion hat Elm einen sehr viel einfacheren Implementierungsansatz als JavaScript. Es muss nicht auf Callbacks zurückgegriffen werden. Stattdessen wird ein Signal (hier die Position der Maus) zu einem Text konvertiert und durch lift aktualisiert, sobald sich der Wert ändert (hier also wenn die Maus bewegt wird). Dieses simple Beispiel zeigt bereits, welche Möglichkeiten einem Programmierer durch Funktional Reaktive Programmierung geboten werden. Sogar das Problem der Callback-Hell kann durch Sprachen wie Elm in einfachen, nachvollziehbaren Code umgewandelt werden. Elm versucht, dem Programmierer gleichzeitig Flexibilität und Expressivitt zu bieten, wobei es gleichzeitig nur auf Abstraktionen zurückgreift, die effizient in einem nichtsequentiellen System implementiert werden können [14]. 3 Pro und Contra der Anwendung Callbacks und Event Handlers sind mächtige Instrumente, die vor allem bei der webspezifischen Programmierung eine sehr große Rolle spielen. Durch sie wird die Kommunikation zwischen Server und Client vereinfacht und effizienter gestaltet. Sie sind die Schnittstelle zwischen User und Skript und verhelfen zu mehr Benutzerfreundlichkeit im Bezug auf die Bedienoberfläche. Wie man sie einsetzt ist schnell gelernt und eingängig. Wie in Kapitel 2.1 schon erwähnt, kann es aber leider zu Problemen kommen, v.a. im Bezug auf die CallbackHell. Deswegen wäre es sinnvoll, Callbacks und Event-Handlers nur bei kurzem oder wenig verschachteltem Code zu verwenden, damit die Nachvollziehbarkeit bestehen bleibt. Als Alternative kann für komplexeren Code Functional Reactive Programming eingesetzt werden. Vor Allem bei webspezifischen Anwendungen hat sich das jedoch noch nicht durchgesetzt. Callbacks und Event-Handlers sind beliebte und verbreitete Strukturmittel, die ihre Nützlichkeit durch ihre vielfältigen Einsatzmöglichkeiten und -gebiete unter Beweis stellen. Ob sie sich auch in Zukunft durchsetzen können oder ob sie durch übersichtliche, aber schwer zu implementierende Konstrukte aus der Funktional Reaktiven Programmierung ersetzt werden, wird sich erst noch zeigen. 13 Literatur [1] Communications of the ACM, 51(1), 2008. [2] Dave Crane, Bear Bibeault, and Jord Sonneveld. Ajax in practice - Das Praxisbuch für die Web 2.0-Entwicklung. Addison-Wesley Verlag, 2008. [3] Evan Czaplicki and Stephen Chong. Asynchronous Functional Reactive Programming for GUIs. Technical report, Harvard University, 2013. [4] Walter Doberenz and Thomas Gewinnus. Visual C# 2010 - Grundlagen und Profiwissen. Carl Hanser Verlag München, 2010. [5] Conal Elliott. Push-pull functional reactive programming. In Haskell Symposium, 2009. [6] David Flanagan. JavaScript: Das umfassende Referenzwerk. O’Reilly Verlag, 1997. [7] M. Hagiya and P. Wadler. Functional and Logic Programming: 8th International Symposium, FLOPS 2006, Fuji-Susono, Japan, April 24-26, 2006, Proceedings. Lecture Notes in Computer Science / Programming and Software Engineering. Springer, 2006. [8] Tom Hughes-Croucher and Mike Wilson. Einführung in Node.js. O’Reilly Verlag, 2012. [9] C.J. Ihrig. Pro Node.js for Developers. Expert’s voice in Web development. Apress, 2013. [10] Zak Ruvalcaba and Mike Murach. Murachs JavaScript and jQuery. Mike Murach & Associates, Inc., 2012. [11] S. Stefanov. JavaScript Patterns. O’Reilly Verlag, 2011. [12] Ralph Steyer. jQuery - DasJavaScript-Framework für interaktives Design. Addison-Wesley Verlag, 2011. [13] Ralph Steyer. JavaScript für Fortgeschrittene - Debugging, PerformanceTsools, OOP. Video-Training, online, 2013. [14] Bruce Tate, Fred Daoud, Jack Moffitt, and Ian Dees. Seven More Languages in Seven Weeks: Languages That Are Shaping the Future. The Pragmatic Bookshelf, 2014. 14