An Evaluation of Reactive Programming and Promises for Structuring Collaborative Web Applications
Total Page:16
File Type:pdf, Size:1020Kb
An Evaluation of Reactive Programming and Promises for Structuring Collaborative Web Applications Kennedy Kambona Elisa Gonzalez Boix Wolfgang De Meuter Software Languages Lab Software Languages Lab Software Languages Lab Vrije Universiteit Brussel Vrije Universiteit Brussel Vrije Universiteit Brussel Brussels, Belgium Brussels, Belgium Brussels, Belgium [email protected] [email protected] [email protected] ABSTRACT gives birth to a variety of challenges. The most notable one JavaScript programs are highly event-driven, resulting in that has recently gained high recognition among JavaScript `asynchronous spaghetti' code that is difficult to maintain enthusiasts is the asynchronous spaghetti[16]: deeply-nested as the magnitude programs written in the language grows. callbacks that have dependencies on data that have been To reduce the effects of this callback hell, various concepts returned from previous asynchronous invocations. have been employed by a number of JavaScript libraries and Since callbacks are common in JavaScript, programmers frameworks. In this paper we investigate the expressive- end up losing the ability to think in the familiar sequential ness of two such techniques, namely reactive extensions and algorithms and end up dealing with an unfamiliar program promises. We have done this by means of a case study con- structure. A program might be forced to shelve program sisting of an online collaborative drawing editor. The editor execution aside and continue when a return value becomes supports advanced drawing features which we try to model available in the unforeseen future while in the meantime ex- using the aforementioned techniques. We then present a ecution continues performing some other computation. This discussion on our overall experience in implementing the ap- structure forces programmers to write their applications in plication using the two concepts. From this, we propose a a nested hierarchy of callbacks, inevitably leading to what roadmap of how to improve support of programming event- is termed as callback hell or the pyramid of doom [21]. driven web applications in JavaScript. In the next sections we present two increasingly popu- lar approaches that have been proposed as a solution to these problems in the JavaScript context: reactive program- Categories and Subject Descriptors ming [9] and the concept of promises [10]. We describe these D.1.5 [Software Programming Techniques]: Object ori- two approaches and apply them on a non-trivial online draw- ented programming; D.3.3 [Programming Languages]: ing application using the standard version of JavaScript, EC- Language Constructs and Features MAScript51. At the same time we also compare the imple- mentations when necessary and observe the lessons learnt Keywords from these experiences. Javascript, Callbacks, Reactive programming, Promises, Event Streams, Behaviours, Futures 2. THE PROBLEM WITH CALLBACKS Callbacks are used in event-driven programming to ob- 1. INTRODUCTION tain results of asynchronous computations. Rather than an JavaScript has been identified as the most dominant lan- application blocking on a potentially indeterministic event, guage in the web [5]. From its conception, the design of the execution semantics of the application shifts from the the JavaScript language was primarily targeted for the web programmer, relinquishing control of the event when the in- browser [17]. As such, the main focus at the time was to vocation happens and registering a method with the intent provide ways to improve user interaction. The language of reacting when the event is performed. therefore provides support for user interaction employing an Utilizing these callbacks in large-scale applications leads event-driven style of programming. to a mix of collocated code fragments that are not easily Though the asynchronous nature of the language lends composable and creates complex flows that force the pro- itself well to building responsive web applications, it also grammer to pass the callbacks around in order to utilize their delayed values as illustrated in Listing 1. In this sce- nario of a chat application, there are three asynchronous calls: the first one registers a user to a chat server (line 1), Permission to make digital or hard copies of all or part of this work for the second asks the server for an available room to join (line personal or classroom use is granted without fee provided that copies are 2) and the third broadcasts an initial chat message (line 3) not made or distributed for profit or commercial advantage and that copies to users in the room. bear this notice and the full citation on the first page. To copy otherwise, to As a result, callbacks become hard to understand, particu- republish, to post on servers or to redistribute to lists, requires prior specific larly when the control flow of an application require handling permission and/or a fee. Dyla ’13, Montpellier, France 1 Copyright 2013 ACM 978-1-4503-2041-2 ...$15.00. As of this writing of multiple events together, or of events that depend on one group of independent asynchronous calls as shown in List- another. The programmer is forced to write boilerplate code ing 3 in the Q library using Q.all. This listing computes in order to orchestrate the asynchronous operations. the Fibonacci of two numbers through promises. Listing 1: Nested Callbacks Listing 3: Composing Promises 1 registerToChatOnServer(username,function(rooms)f 1 fibpromise = Q.all([ 2 joinAvailableRoom(rooms, function(roomname)f 2 computeFibonacci(n−1), 3 sendChatToAll(roomname, msg, function(reply)f 3 computeFibonacci(n−2) 4 showChatReply(reply); 4 ]); 5 g) 5 fibpromise.spread(function (result1, result2 ) f 6 g) 6 //resolver for both operations.. 7 g); 7 console.log( result1 + result2); 8 g),function(err)f 9 //error occurred 3. JAVASCRIPT PROMISES 10 g); One approach to deal with the problem imposed by call- To react to fibpromise we attach a resolver in line 5. The backs has been in the use of promises. In 1976, Daniel Fried- Q library provides Q.spread, which allows the arguments of man and D. Wise [10] originally proposed the term promise the individual elements in a composition to be sent to the as a proxy object that represents an unknown result that is resolver. In this case, fibpromise awaits for both fibonacci yet to be computed. sequences to be computed before it passes the results to the Most implementations regard promises as immutable once- resolving function (line 5) which then sums the results (line resolved promises as per the CommonJS Promises/A spec- 7). ification [12], which states that a promise can be rejected or resolved once and only once, to prevent unanticipated 3.2 Alternative Approaches changes in behaviour. They furthermore encapsulate asy- Within the domain of web development, there exist other nchronous actions, acting much like a value returned from frameworks which provide the abstractions for implemen- the result of a computation only that the value may not tation of promises apart from the Q library. These include be available at the time. Promises therefore represent the AngularJs3, a JavaScript MVC-based framework for web ap- eventual completion and result of a single asynchronous op- plications, and Dart4, a class-based web programming lan- eration. guage. The most common term used for JavaScript promises are The AngularJs promises were inspired by Q and are simi- the thenables, as a developer uses then to attach callbacks lar to Q's promises. However, the functionality provided by to a promise when it is either fulfilled or an exception is AngularJs' promises are a subset to those provided in Q in 2 realised . In line 1 of Listing 2 we use Q.fcall of the order to make them accustomed to the framework's execu- Q [21] library which creates a promise from the returned tion style. It therefore lacks useful features such as promise value of asynchronous invocations. It is important to note chaining. that then also returns a promise, implying that promises can Dart promises (recently re-implemented as Dart futures), be chained together. Listing 2 shows chaining of promises are also similar to Q's promises, with Completers used to (lines 1-4) using the Q library. The listing performs similar manage the futures. Dart also contains a Futures interface functionality as the previous scenario of Listing 1, where pre- for easily creating multiple future objects. We however did defined functions in lines 1-3 perform asynchronous requests not use Dart since the aim of this experiment was to focus to the server. on mainstream technologies, i.e. JavaScript. Dart is a sep- arate specialized language and currently lacks full support Listing 2: .then() in Q for integration with normal JavaScript applications. 1 Q. fcall (registerToChatOnServer) Outside the web, promises are widely employed as abstrac- 2 .then(joinAvailableRoom) tions in the domain of concurrent and distributed languages. 3 .then(sendChat) The distributed, actor-based language AmbientTalk [6] uses 4 .then(function (reply)f similar concepts known as futures for non-blocking remote 5 showChatReply(reply) invocations, which can composed by grouping. Argus [13] 6 g,function (error)f 7 // catch error from all async operations provides promise pipelining, similar to the aforementioned 8 g) promise chaining. Fignale [19] provides future combinators 9 .done(); to compose delayed actions in a scatter/gather pattern. 3.3 Summary 3.1 Composing Promises Promises provide an elegant fall-back from the nested call- In most libraries, promises are