Futures and Rx Observables: powerful abstractions for consuming web services asynchronously...
-
Upload
chris-richardson -
Category
Technology
-
view
5.407 -
download
0
description
Transcript of Futures and Rx Observables: powerful abstractions for consuming web services asynchronously...
@crichardson
Consuming web services asynchronously with Futures
and Rx ObservablesChris Richardson
Author of POJOs in ActionFounder of the original CloudFoundry.com
@[email protected]://plainoldobjects.com
@crichardson
Presentation goal
Learn how to use (Scala) Futures and Rx Observables to write
simple yet robust and scalable concurrent code
@crichardson
About Chris
@crichardson
(About Chris)
@crichardson
About Chris()
@crichardson
About Chris
@crichardson
About Chris
http://www.theregister.co.uk/2009/08/19/springsource_cloud_foundry/
@crichardson
About Chris
@crichardson
Agenda
The need for concurrency
Simplifying concurrent code with Futures
Taming callback hell with JavaScript promises
Consuming asynchronous streams with Reactive Extensions
@crichardson
Let’s imagine you are building an online store
@crichardson
Shipping
Recomendations
ProductInfo
Reviews
@crichardson
Reviews
ProductInfo
Sales ranking
@crichardson
Related books
Viewing history
Forum
@crichardson
+ mobile apps
@crichardson
Application architecture
Product Info ServiceDesktop browser
Native mobile client
REST
REST
REST
Recomendation Service
Review Service
Front end server
API gatewayREST
Mobile browser
Web ApplicationHTML
@crichardson
Handling getProductDetails()
Front-end server
Product Info Service
RecommendationsService
ReviewService
getProductInfo()
getRecommendations()
getReviews()
getProductDetails()
@crichardson
Handling getProductDetails() - sequentially
Front-end server
Product Info Service
RecommendationsService
ReviewService
getProductInfo()
getRecommendations()
getReviews()
getProductDetails()
Higher response time :-(
@crichardson
Handling getProductDetails() - in parallel
Front-end server
Product Info Service
RecommendationsService
ReviewService
getProductInfo()
getRecommendations()
getReviews()
getProductDetails()
Lower response time :-)
@crichardson
Implementing a concurrent REST client
Thread-pool based approach
executorService.submit(new Callable(...))
Simpler but less scalable - lots of idle threads consuming memory
Event-driven approach
NIO with completion callbacks
More complex but more scalable
@crichardson
The front-end server must handle partial failure of backend services
Front-end server
Product Info Service
RecommendationsService
ReviewService
getProductInfo()
getRecommendations()
getReviews()
getProductDetails() XHow to provide a
good user experience?
@crichardson
Agenda
The need for concurrency
Simplifying concurrent code with Futures
Taming callback hell with JavaScript promises
Consuming asynchronous streams with Reactive Extensions
@crichardson
Futures are a great concurrency abstraction
http://en.wikipedia.org/wiki/Futures_and_promises
@crichardson
How futures work
Outcome
Future
Client
get
Asynchronous operation
set
initiates
@crichardson
Benefits
Client does not know how the asynchronous operation is implemented
Client can invoke multiple asynchronous operations and gets a Future for each one.
@crichardson
REST client using Spring @Async
@Componentclass ProductInfoServiceImpl extends ProducInfoService { val restTemplate : RestTemplate = ...
@Async def getProductInfo(productId: Long) = { new AsyncResult(restTemplate.getForObject(....)...) }
}
Execute asynchronously in thread pool
A Future containing a value
trait ProductInfoService { def getProductInfo(productId: Long): java.util.concurrent.Future[ProductInfo]}
@crichardson
ProductDetailsService@Componentclass ProductDetailsService @Autowired()(productInfoService: ProductInfoService, reviewService: ReviewService, recommendationService: RecommendationService) {
def getProductDetails(productId: Long): ProductDetails = { val productInfoFuture = productInfoService.getProductInfo(productId)
}
}
val recommendationsFuture = recommendationService.getRecommendations(productId)val reviewsFuture = reviewService.getReviews(productId)
val productInfo = productInfoFuture.get(300, TimeUnit.MILLISECONDS) val recommendations = recommendationsFuture.get(10, TimeUnit.MILLISECONDS) val reviews = reviewsFuture.get(10, TimeUnit.MILLISECONDS)
ProductDetails(productInfo, recommendations, reviews)
@crichardson
ProductController
@Controllerclass ProductController @Autowired()(productDetailsService : ProductDetailsService) {
@RequestMapping(Array("/productdetails/{productId}")) @ResponseBody def productDetails(@PathVariable productId: Long) =
productDetailsService.getProductDetails(productId)
@crichardson
Not bad but...
val productInfo = productInfoFuture.get(300, TimeUnit.MILLISECONDS)
Blocks Tomcat thread until Future completes
Not so scalable :-(
@crichardson
... and also...
Java Futures work well for a single-level of asynchronous execution
BUT #fail for more complex, scalable scenarios
Difficult to compose and coordinate multiple concurrent operations
http://techblog.netflix.com/2013/02/rxjava-netflix-api.html
@crichardson
Better: Futures with callbacks ⇒ no blocking!
val f : Future[Int] = Future { ... } f onSuccess { case x : Int => println(x) } f onFailure { case e : Exception => println("exception thrown") }
Guava ListenableFutures, Spring 4 ListenableFutureJava 8 CompletableFuture, Scala Futures
@crichardson
Even better: Composable Futures
val f1 = Future { ... ; 1 }val f2 = Future { ... ; 2 }
val f4 = f2.map(_ * 2)assertEquals(4, Await.result(f4, 1 second))
val fzip = f1 zip f2assertEquals((1, 2), Await.result(fzip, 1 second))
Transforms Future
Combines two futures
Scala, Java 8 CompletableFuture (partially)
@crichardson
Scala futures are Monadsdef callB() : Future[...] = ...def callC() : Future[...] = ...def callD() : Future[...] = ...
val result = for { (b, c) <- callB() zip callC(); d <- callD(b, c) } yield d
result onSuccess { .... }
Two calls execute in parallel
And then invokes D
Get the result of D
@crichardson
Scala Future + RestTemplateimport scala.concurrent.Future
@Componentclass ProductInfoService {
def getProductInfo(productId: Long): Future[ProductInfo] = { Future { restTemplate.getForObject(....) } }
}
Executes in thread pool
Scala Future
@crichardson
Scala Future + RestTemplateclass ProductDetailsService @Autowired()(....) {
def getProductDetails(productId: Long) = { val productInfoFuture = productInfoService.getProductInfo(productId) val recommendationsFuture = recommendationService.getRecommendations(productId) val reviewsFuture = reviewService.getReviews(productId)
for (((productInfo, recommendations), reviews) <- productInfoFuture zip recommendationsFuture zip reviewsFuture) yield ProductDetails(productInfo, recommendations, reviews) }
}
Non-blocking!
@crichardson
Async Spring MVC + Scala Futures@Controllerclass ProductController ... {
@RequestMapping(Array("/productdetails/{productId}")) @ResponseBody def productDetails(@PathVariable productId: Long) = { val productDetails = productDetailsService.getProductDetails(productId)
val result = new DeferredResult[ProductDetails] productDetails onSuccess { case r => result.setResult(r) } productDetails onFailure { case t => result.setErrorResult(t) } result }
Convert Scala Future to Spring MVC
DeferredResult
@crichardson
Servlet layer is asynchronous but the backend uses thread pools
⇒Need event-driven REST client
@crichardson
About the Reactor pattern
Defined by Doug Schmidt in 1995
Pattern for writing scalable servers
Alternative to thread-per-connection model
Single threaded event loop dispatches events on handles (e.g. sockets, file descriptors) to event handlers
@crichardson
Reactor pattern structure
Event Handlerhandle_event(type)get_handle()
Initiation Dispatcherhandle_events() register_handler(h)
select(handlers)for each h in handlers h.handle_event(type)end loop
handle
Synchronous Event Demultiplexer
select()
owns
notifies
uses
handlers
Application
creates
@crichardson
Java NIO Selectors = Reactor pattern
But that’s super low-level :-(
@crichardson
New in Spring 4
Mirrors RestTemplate
Methods return a ListenableFuture = JDK 7 Future + callback methods
Can use HttpComponents NIO-based AsyncHttpClient
Spring AsyncRestTemplate
@crichardson
Using the AsyncRestTemplate
http://hc.apache.org/httpcomponents-asyncclient-dev/
val asyncRestTemplate = new AsyncRestTemplate( new HttpComponentsAsyncClientHttpRequestFactory())
override def getProductInfo(productId: Long) = {
val listenableFuture = asyncRestTemplate.getForEntity("{baseUrl}/productinfo/{productId}", classOf[ProductInfo], baseUrl, productId)
toScalaFuture(listenableFuture).map { _.getBody }}
Convert to Scala Future and get entity
@crichardson
Converting ListenableFuture to Scala Future
implicit def toScalaFuture[T](f : ListenableFuture[T]) : Future[T] = { val p = promise[T]
f.addCallback(new ListenableFutureCallback[T] { def onSuccess(result: T) { p.success(result)} def onFailure(t: Throwable) { p.failure(t) } }) p.future }
The producer side of Scala Futures
Supply outcomeReturn future
@crichardson
Now everything is non-blocking :-)
@crichardson
If recommendation service is down...
Never responds ⇒ front-end server waits indefinitely
Consumes valuable front-end server resources
Page never displayed and customer gives up
Returns an error
Error returned to the front-end server ⇒ error page is displayed
Customer gives up
@crichardson
Fault tolerance at NetflixNetwork timeouts and retries
Invoke remote services via a bounded thread pool
Use the Circuit Breaker pattern
On failure:
return default/cached data
return error to caller
Implementation: https://github.com/Netflix/Hystrix
http://techblog.netflix.com/2012/02/fault-tolerance-in-high-volume.html
@crichardson
Using Hystrix@Componentclass ProductInfoServiceImpl extends ProductInfoService {
val restTemplate = RestTemplateFactory.makeRestTemplate() val baseUrl = ...
class GetProductInfoCommand(productId: Long)extends HystrixCommand[ProductInfo](....) {
override def run() = restTemplate. getForEntity("{baseUrl}/productinfo/{productId}",
classOf[ProductInfo], baseUrl, productId).getBody
}
def getProductInfoUsingHystrix(productId: Long) : Future[ProductInfo] = { new GetProductInfoCommand(productId).queue() }
}
Runs in thread pool
Returns JDK Future
@crichardson
But how to accomplish this with event-driven code
@crichardson
How to handling partial failures?
productDetailsFuture zip recommendationsFuture zip reviewsFuture
Fails if any Future has failed
@crichardson
Handling partial failuresval recommendationsFuture = recommendationService. getRecommendations(userId,productId). recover { case _ => Recommendations(List()) }
“catch-like” Maps Throwable to value
@crichardson
Implementing a Timeout
resultFuture onSuccess { case r => result.setResult(r) }resultFuture onFailure { case t => result.setErrorResult(t) }
No timeout - callbacks might never be invoked :-(
Await.result(resultFuture, timeout)
Blocks until timeout:-(
@crichardson
Non-blocking Timeout
http://eng.42go.com/future-safefuture-timeout-cancelable/
object TimeoutFuture { def apply[T](future: Future[T], onTimeout: => Unit = Unit) (implicit ec: ExecutionContext, after: Duration): Future[T] = { val timer = new HashedWheelTimer(10, TimeUnit.MILLISECONDS) val promise = Promise[T]() val timeout = timer.newTimeout(new TimerTask { def run(timeout: Timeout){ onTimeout promise.failure(new TimeoutException(s"Future timed out after ${after.toMillis}ms")) } }, after.toNanos, TimeUnit.NANOSECONDS) Future.firstCompletedOf(Seq(future, promise.future)). tap(_.onComplete { case result => timeout.cancel() }) } }
val future = ...val timedoutFuture = TimeoutFuture(future)(executionContext, 200.milleseconds) Timer fails
promise
Outcome of first completed
@crichardson
Using the Akka Circuit Breaker
import akka.pattern.CircuitBreaker
val breaker = new CircuitBreaker(actorSystem.scheduler, maxFailures = 1, callTimeout = 100.milliseconds, resetTimeout = 1.minute)
val resultFuture = breaker. withCircuitBreaker{ asynchronousOperationReturningFuture() }
@crichardson
Limiting # of simultaneous requestsval limiter = new ConcurrencyLimiter(maxConcurrentRequests=10, maxQueueSize=30)
val resultFuture = limiter.withLimit { asynchronousOperationReturningFuture() }class ConcurrencyLimiter ...
val concurrencyManager : ActorRef = ...
def withLimit[T](body : => Future[T])(...) = (concurrencyManager ? ConcurrentExecutionManager.Request { () => body }).mapTo[T]
@crichardson
Putting it all together@Componentclass ProductInfoServiceImpl @Autowired()(...) extends ProductService {
val limiter = new ConcurrencyLimiter(...)
val breaker = new CircuitBreaker(...)
override def getProductInfo(productId: Long) = { ... breaker.withCircuitBreaker { limiter.withLimit { TimeoutFuture { ... AsyncRestTemplate.get ... } } } }
@crichardson
Agenda
The need for concurrency
Simplifying concurrent code with Futures
Taming callback hell with JavaScript promises
Consuming asynchronous streams with Reactive Extensions
@crichardson
@crichardson
Why solve this problem for JavaScript?
Browser invokes web services
Implement front-end server/API gateway using NodeJS!
@crichardson
What’s NodeJS?
Designed for DIRTy apps
@crichardson
NodeJS
JavaScript
Reactor pattern
Modules
@crichardson
Asynchronous JavaScript code = callback hell
Scenarios:
Sequential: A ⇒ B ⇒ C
Fork and join: A and B ⇒ C
Code quickly becomes very messy
@crichardson
Callback-based HTTP client
request = require("request") handler = (error, clientResponse, body) -> if clientResponse.statusCode != 200 // ERROR else // SUCCESS
request("http://.../productdetails/" + productId, handler)
@crichardson
Messy callback codegetProductDetails = (req, res) -> productId = req.params.productId result = {productId: productId} makeHandler = (key) -> (error, clientResponse, body) -> if clientResponse.statusCode != 200 res.status(clientResponse.statusCode) res.write(body) res.end() else result[key] = JSON.parse(body) if (result.productInfo and result.recommendations and result.reviews) res.json(result)
request("http://localhost:3000/productdetails/" + productId, makeHandler("productInfo")) request("http://localhost:3000/recommendations/" + productId, makeHandler('recommendations')) request("http://localhost:3000/reviews/" + productId, makeHandler('reviews'))
app.get('/productinfo/:productId', getProductInfo)
Returns a callback function
Have all three requests completed?
@crichardson
Simplifying code with Promises (a.k.a. Futures)
Functions return a promise - no callback parameter
A promise represents an eventual outcome
Use a library of functions for transforming and composing promises
Promises/A+ specification - http://promises-aplus.github.io/promises-spec
@crichardson
Basic promise API
promise2 = promise1.then(fulfilledHandler, errorHandler, ...)
called when promise1 is fulfilledreturns a promise or value
resolved with outcome of callbackcalled when promise1 is rejected
returns a promise or value
About when.jsRich API = Promises spec + use full extensions
Creation:
when.defer()
when.reject()...
Combinators:
all, some, join, map, reduce
Other:
Calling non-promise code
timeout
....
https://github.com/cujojs/when
@crichardson
Simpler promise-based code
rest = require("rest")
@httpClient = rest.chain(mime).chain(errorCode);
getProductInfo : (productId) -> @httpClient path: "http://.../productinfo/" + productId
Returns a promise
No ugly callbacks
@crichardson
Simpler promise-based code class ProductDetailsService getProductDetails: (productId) -> makeProductDetails = (productInfo, recommendations, reviews) -> details = productId: productId productDetails: productInfo.entity recommendations: recommendations.entity reviews: reviews.entity details responses = [@getProductInfo(productId), @getRecommendations(productId),
@getReviews(productId)] all(responses).spread(makeProductDetails)
Array[Promise] ⇒ Promise[Array]
@crichardson
Simpler promise-based code: HTTP handlergetProductDetails = (req, res) -> productId = req.params.productId
succeed = (productDetails) -> res.json(productDetails)
fail = (something) -> res.send(500, JSON.stringify(something.entity || something))
productDetailsService. getDetails(productId). then(succeed, fail)
app.get('/productdetails/:productId', getProductDetails)
@crichardson
Writing robust client code
@crichardson
Implementing timeouts
timeout = require("when/timeout")withTimeout = (promise) -> timeout(300, promise)getProductDetails = (productId) -> ... withTimeout(client(...))
Creates a new promiseOriginal promise must complete within 300 msec
@crichardson
Recovering from failures
getRecommendations(productId).otherwise( -> {})
Invoked to return default value if getRecommendations() fails
@crichardson
Limiting # of concurrent requests
ConcurrencyLimiter = require("./concurrencylimiter").ConcurrencyLimiter
limiter = new ConcurrencyLimiter(maxQueueSize = 100, maxConcurrency = 10)
getProductDetails = (productId) -> ... limiter.withLimit( -> client(...))
Homegrown code
@crichardson
Circuit breaker
CircuitBreaker = require("./circuitbreaker").CircuitBreaker
productCircuitBreaker = new CircuitBreaker()
getProductDetails = (productId) -> ... productCircuitBreaker.withCircuitBreaker( -> client(...))
Homegrown code
@crichardson
Putting it all together
getProductDetails: (productId) -> ... responses = [@getProductInfo(productId), @getRecommendations(productId).otherwise(() -> {}), @getReviews(productId).otherwise(() -> {})] all(responses).spread(succeed)
getProductInfo = (productId) -> limiter.withLimit -> productCircuitBreaker.withCircuitBreaker -> withTimeout client path: "http://..../productinfo/" + productd
@crichardson
Agenda
The need for concurrency
Simplifying concurrent code with Futures
Taming callback hell with JavaScript promises
Consuming asynchronous streams with Reactive Extensions
@crichardson
Let’s imagine you have a stream of tradesand
you need to calculate the rolling average price of each stock
@crichardson
Spring Integration + Complex event processing (CEP) engine is a good
choice
@crichardson
But where is the high-level abstraction that solves this problem?
@crichardson
Future[List[T]]
Not applicable to infinite streams
@crichardson
Introducing Reactive Extensions (Rx)
The Reactive Extensions (Rx) is a library for composing asynchronous and event-based programs using
observable sequences and LINQ-style query operators. Using Rx, developers represent asynchronous data streams with Observables , query asynchronous
data streams using LINQ operators , and .....
https://rx.codeplex.com/
@crichardson
About RxJava
Reactive Extensions (Rx) for the JVM
Implemented in Java
Adaptors for Scala, Groovy and Clojure
https://github.com/Netflix/RxJava
@crichardson
RxJava core concepts
class Observable<T> { Subscription subscribe(Observer<T> observer) ...}
interface Observer<T> {void onNext(T args)void onCompleted()void onError(Throwable e)
}
Notifies
An asynchronous stream of items
Used to unsubscribe
@crichardson
Comparing Observable to...Observer pattern - similar but adds
Observer.onComplete()
Observer.onError()
Iterator pattern - mirror image
Push rather than pull
Future - similar but
Represents a stream of multiple values
@crichardson
So what?
@crichardson
Transforming Observables
class Observable<T> {
Observable<T> take(int n); Observable<T> skip(int n); <R> Observable<R> map(Func1<T,R> func) <R> Observable<R> flatMap(Func1<T,Observable<R>> func) Observable<T> filter(Func1<T,java.lang.Boolean> predicate)
...}
Scala-collection style methods
@crichardson
Transforming observables
class Observable<T> {
<K> Observable<GroupedObservable<K,T>> groupBy(Func1<T,K> keySelector) ...}
Similar to Scala groupBy except results are emitted as items arrive
Stream of streams!
@crichardson
Combining observables
class Observable<T> {
static <R,T1,T2> Observable<R> zip(Observable<T0> o1, Observable<T1> o2, Func2<T1,T2,R> reduceFunction)
...}
Invoked with pairs of items from o1 and o2
Stream of results from reduceFunction
@crichardson
Creating observables from data
class Observable<T> { static Observable<T> from(Iterable<T> iterable]); static Observable<T> from(T items ...]); static Observable<T> from(Future<T> future]); ...}
@crichardson
Creating observables from event sources
Observable<T> o = Observable.create( new SubscriberFunc<T> () { Subscription onSubscribe(Observer<T> obs) {
... connect to event source....
return new Subscriber () { void unsubscribe() { ... };
}; });
Called once for each Observer
Called when unsubscribing
Arranges to call obs.onNext/onComplete/...
@crichardson
Using Rx Observables instead of Futures
@crichardson
Rx-based ProductInfoService@Componentclass ProductInfoService override def getProductInfo(productId: Long) = {
val baseUrl = ...
val responseEntity = asyncRestTemplate.getForEntity(baseUrl + "/productinfo/{productId}", classOf[ProductInfo], productId)
toRxObservable(responseEntity).map { (r : ResponseEntity[ProductInfo]) => r.getBody }
}
@crichardson
ListenableFuture ⇒ Observable implicit def toRxObservable[T](future: ListenableFuture[T]) : Observable[T] = { def create(o: Observer[T]) = { future.addCallback(new ListenableFutureCallback[T] { def onSuccess(result: T) { o.onNext(result) o.onCompleted() }
def onFailure(t: Throwable) { o.onError(t) } }) new Subscription { def unsubscribe() {} } }
Observable.create(create _) }
Supply single value
Indicate failure
Do nothing
@crichardson
Rx - ProductDetailsService@Componentclass ProductDetailsService @Autowired() (productInfoService: ProductInfoService, reviewService: ReviewService, ecommendationService: RecommendationService) {
def getProductDetails(productId: Long) = { val productInfo = productInfoService.getProductInfo(productId) val recommendations =
recommendationService.getRecommendations(productId) val reviews = reviewService.getReviews(productId)
Observable.zip(productInfo, recommendations, reviews, (p : ProductInfo, r : Recommendations, rv : Reviews) =>
ProductDetails(p, r, rv)) }
}
Just like Scala
Futures
@crichardson
Rx - ProductController
@Controllerclass ProductController
@RequestMapping(Array("/productdetails/{productId}")) @ResponseBody def productDetails(@PathVariable productId: Long) = toDeferredResult(productDetailsService.getProductDetails(productId))
@crichardson
Rx - Observable ⇒ DeferredResult implicit def toDeferredResult[T](observable : Observable[T]) = { val result = new DeferredResult[T] observable.subscribe(new Observer[T] { def onCompleted() {}
def onError(e: Throwable) { result.setErrorResult(e) }
def onNext(r: T) { result.setResult(r.asInstanceOf[T]) } }) result }
@crichardson
RxObservables vs. Futures
Much better than JDK 7 futures
Comparable to Scala Futures
Rx Scala code is slightly more verbose
@crichardson
Making this code robust
Hystrix works with Observables (and thread pools)
But
For event-driven code we are on our own
@crichardson
Back to the stream of Trades averaging example...
@crichardson
AMQP messages ⇒ Observables 1
<amqp:inbound-channel-adapter ... />
<int:channel id="inboundEventsChannel"/> <int:service-activator input-channel="inboundEventsChannel" ref="amqpToObservableAdapter" method="handleMessage"/>
@crichardson
AMQP messages ⇒ Observables 2class AmqpToObservableAdapter (actorRef : observerManager) {
def createObservable() = Observable.create((o: Observer[_ >: String]) => {
observerManager ! Subscribe(o) new Subscription { override def unsubscribe() { observerManager ! Unsubscribe(o) } } })
def handleMessage(message : String) { observerManager ! Message(message) }}
Manages and notifies observers
@crichardson
Calculating averages
Observable<AveragePrice> calculateAverages(Observable<Trade> trades) { ...}
class Trade { private String symbol; private double price;
class AveragePrice { private String symbol; private double averagePrice;
@crichardson
Using groupBy()APPL : 401 IBM : 405 CAT : 405 APPL: 403
groupBy( (trade) => trade.symbol)
APPL : 401
IBM : 405
CAT : 405 ...
APPL: 403
Observable<GroupedObservable<String, Trade>>
Observable<Trade>
@crichardson
Using window()APPL : 401 APPL : 405 APPL : 405 ...
APPL : 401 APPL : 405 APPL : 405
APPL : 405 APPL : 405 APPL : 403
APPL : 405 ...
window(...)
Observable<Trade>
Observable<Observable<Trade>>
N secs
N secs
M secs
@crichardson
Using reduce()
APPL : 402 APPL : 405 APPL : 405
APPL : 404
reduce(sumCalc) / length
Observable<Trade>
Observable<AveragePrice> Singleton
@crichardson
Using merge()
merge()
APPL : 401
IBM : 405
CAT : 405 ...
APPL: 403
APPL : 401 IBM : 405 CAT : 405 APPL: 403
Observable<Observable<AveragePrice>>
Observable<AveragePrice>
@crichardson
RxJS / NodeJS examplevar rx = require("rx");
function tailFile (fileName) { var self = this;
var o = rx.Observable.create(function (observer) { var watcher = self.tail(fileName, function (line) { observer.onNext(line); }); return function () { watcher.close(); } }); return o.map(parseLogLine);}
function parseLogLine(line) { ... }
@crichardson
Rx in the browser - implementing completion
TextChanges(input) .DistinctUntilChanged() .Throttle(TimeSpan.FromMilliSeconds(10)) .Select(word=>Completions(word)) .Switch() .Subscribe(ObserveChanges(output));
Your Mouse is a Databasehttp://queue.acm.org/detail.cfm?id=2169076
Ajax call returning Observable
Change events ⇒ Observable
Display completions
@crichardson
Summary
Consuming web services asynchronously is essential
Scala-style composable Futures are a powerful concurrency abstraction
Rx Observables are even more powerful
Rx Observables are a unifying abstraction for a wide variety of use cases