Technology

More Functional Reactive Programming Recipes


A common need in FRP is pulling in another stream inside another stream and combining the results. In some frameworks, this is super easy.

For example, in BaconJS, it’s just:

  const stream1 = //...
  const stream2 = //...

  stream1
    .map(stream1 => //do sometin' funky w/ stream1)
    .flatMap(stream1 => Bacon.combineTemplate({stream1, stream2})) //combineTemplate to the rescue
    .map(template => {
        //template now has values from stream1 and stream2
        const stream1 = template.stream1;
        const stream2 = template.stream2;
    })
    //...

In case you don’t know, “flatMap” returns a value of a stream. Bacon.combineTemplate returns a stream, so you need “flatMap” to extract its value. (Promises on the other hand, don’t make that distinction).

There are two nice things about this solution:

  1. you don’t have to pull in stream2 until you need it.
  2. you can access stream values by name

What about other frameworks?

HighlandJS and Kefir

  const stream1 = //....
  const stream2 = //....
  
  stream1
    .map(stream1 => //do something funky)
    .zip(stream2)
    .map(template => {
        //template now has values from stream1 and stream2
        const stream1 = template.stream1;
        const stream2 = template.stream2;
    })
    //...

The only thing you lose is referencing values by property name.

Bluebird

In Bluebird, you have promises instead of streams.
Also, “map” doesn’t work like in FRP—it works only on arrays. Instead, use “then”.

  const promise1 = //....
  const promise2 = //....
  
  promise1
    .then(promise1 => //do something funky)
    .then(promise1 => Promise.join(promise1, promise2))
    .then(template => {
        //template now has values from promise1 and promise2
        const promise1 = template[0];
        const promise2 = template[1];
    })
    //...

Shootout: BaconJS vs HighlandJS vs Kefir vs Bluebird

In a totally non-scientific study, I tested the performance of each framework (indirectly) by setting up a Restify server that combines two streams/promises and returns the results. Each server received 10,000 requests.

You can see the tests here: https://gist.github.com/frankandrobot/9015b87140e2e6cf55c3
Apache Bench tested the servers: ab -n 10000 -c 100 http://127.0.0.1:3000/

Here are the results:

BaconJS:                3.471 seconds
HighlandJS:             3.186 seconds
Bluebird:               1.633 seconds
BaconJS (with zip):     2.816 seconds
Kefir:                  2.027 seconds

Winner: Bluebird

Second place: Kefir
Third place: BaconJS
Fourth place: HighlandJS

Not surprisingly BaconJS#combineTemplate came last—you pay for the convenience.
Bluebird came first because it’s a framework that’s been designed for server-side performance. HighlandJS is an ambitious (and interesting) proof of concept, and it’s performance shows. I should point out that “streams” generalize the concept of “Promises”, so a Stream library will be more complex than a Promise-based one. That’s why it’s not surprising that Kefir lost out to Bluebird.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s