This is the first stable release of RxJS version 2.3. This has been a long time in the making and thank you to the community for help making this happen. There are a lot of exciting things in store for RxJS in this release including a custom build system, a commitment to standards, as well as handy things such as smaller Rx-Lite builds.
There are many changes in this release including:
- A Commitment to Standards
- Custom Builds
- A lighter Lite
- New Operators
- Operator Changes
A Commitment to Standards
We on the RxJS team are committed to standards put out by TC39 and the JavaScript community. We will try to add features to take advantage of browser and runtime features as they become available, allowing RxJS to really shine!
Promises Promises
For example, you will find many operators that gladly accept Promise
objects as parameters which are then converted to RxJS Observable
objects. RxJS will use the native Promise
object if present in the browser, but can also be configured to use your own library as long as it follows the ES6 standard for construction. Such libraries include RSVP, Q, when.js, and more.
To specify which promise library to use, you can specify it in the Rx.config.Promise
property. Note that this is only used if you use Rx.Observable.prototype.toPromise
operator.
Some of the operators that support promises natively include:
Rx.Observable.amb
|Rx.Observable.prototype.amb
Rx.Observable.case
Rx.Observable.catch
|Rx.Observable.prototype.catch
Rx.Observable.combineLatest
|Rx.Observable.prototype.combineLatest
Rx.Observable.concat
|Rx.Observable.prototype.concat
Rx.Observable.prototype.concatMap
Rx.Observable.prototype.concatMapObserver
Rx.Observable.defer
Rx.Observable.prototype.flatMap
Rx.Observable.prototype.flatMapLatest
Rx.Observable.forkJoin
|Rx.Observable.prototype.forkJoin
Rx.Observable.if
Rx.Observable.merge
Rx.Observable.prototype.mergeAll
Rx.Observable.onErrorResumeNext
|Rx.Observable.prototype.onErrorResumeNext
Rx.Observable.prototype.selectMany
Rx.Observable.prototype.selectSwitch
Rx.Observable.prototype.sequenceEqual
Rx.Observable.prototype.skipUntil
Rx.Observable.startAsync
Rx.Observable.prototype.switch
Rx.Observable.prototype.takeUntil
Rx.Observable.prototype.throttleWithSelector
Rx.Observable.prototype.timeoutWithSelector
Rx.Observable.while
Rx.Observable.prototype.window
Rx.Observable.zip
|Rx.Observable.prototype.zip
Array Extras Redux
In addition, we have implemented a number of operators on Observable
to mimic what is coming in ES Next for Arrays including:
Rx.Observable.from
Rx.Observable.of
Rx.Observable.prototype.contains
Rx.Observable.prototype.indexOf
Maps and Sets
We also want to support the new data structures coming in a browser near you. Consider that we already have Rx.Observable.prototype.toArray
, we will now support converting to a Set
or Map
if they are available in your environment via:
Rx.Observable.prototype.toMap
Rx.Observable.prototype.toSet
Generators Support
But, we're not done yet, because now we also have support for generators with Rx.Observable.spawn
which allows you to use RxJS in an async/await style in addition to Promises and callbacks.
var Rx = require('rx');
var request = require('request').request;
var get = Rx.Observable.fromNodeCallback(request);
Rx.spawn(function* () {
var data;
try {
var data = yield get('http://bing.com').retry(3);
} catch (e) {
console.log('Error %s', e);
}
console.log(data);
});
You can also use generators with Rx.Observable.from
as well, for example this Fibonacci server.
function* fibonacci(){
var fn1 = 1;
var fn2 = 1;
while (1){
var current = fn2;
fn2 = fn1;
fn1 = fn1 + current;
yield current;
}
}
Rx.Observable.from(fibonacci())
.take(10)
.subscribe(function (x) {
console.log('Value: %s', x);
});
//=> Value: 1
//=> Value: 1
//=> Value: 2
//=> Value: 3
//=> Value: 5
//=> Value: 8
//=> Value: 13
//=> Value: 21
//=> Value: 34
//=> Value: 55
And we're just getting started, with looking at Object.observe
and other places where RxJS can make your development experience better.
Custom Builds
Custom builds are an exciting new piece of RxJS to allow you to include the operators you wish. We have many files that contain many operators, however, it may come down to only wanting a handful of them to keep the size of your file to a minimum. With the rx-cli
project, you have that ability to specify methods, either directly in the command prompt or in a file, and also specify whether you want to have a compatibility build to handle older browsers.
For example, you can specify you with only just a few operators to be included.
$ {sudo} npm install -g rx-cli
$ rx --compat --methods map,flatmap,flatmaplatest,takeuntil,fromevent,filter
This is still an early version and feedback is welcome!
A Liter Lite`
One main goal of RxJS to make the most used operators easily available in a smaller sized package, as in rx.lite.js
. In this release, we have made rx.lite.js
even smaller, down to 9kb gzipped.
To that end, the following time-bsed operators have been removed from rx.lite.js
entirely:
Rx.Observable.prototype.delaySubscription
Rx.Observable.prototype.delayWithSelector
Rx.Observable.prototype.skipLastWithTime
Rx.Observable.prototype.skipUntilWithTime
Rx.Observable.prototype.takeLastWithTime
Rx.Observable.prototype.takeLastBufferWithTime
Rx.Observable.prototype.takeUntilWithTime
Rx.Observable.prototype.throttleWithSelector
Rx.Observable.prototype.timeoutWithSelector
If you still need those operators, it's very easy to just pull in rx.time.js
which has all of those operators. In addition, anything that was in rx.js
that is not in rx.lite.js
is kept in rx.lite.extras.js
to minimize size once again.
New Operators
RxJS added a few new operators in this release to keep parity with RxJava and Rx .NET.
There are the aforementioned operators from the Array#extras and ES Next features including:
Rx.Observable.from
Rx.Observable.of
Rx.Observable.prototype.contains
Rx.Observable.prototype.indexOf
There are other new operators that were introduced to bring compatibility with the Reactive Extensions for .NET:
Observable.selectManyObserver
/Observable.flatMapObserver
Observable.selectConcatObserver
/Observable.concatMapObserver
Operator Changes
There were also a number of changes that were introduced to operators that altered their behavior from version 2.2.
Node.js Support
Node.js support grew during this release to further support the new Streams. To that end, we made the following changes:
- Allowed
objectMode
to be set so that items are pushed in their native format instead of in aBuffer
orString
when usingRx.Node.writeToStream
- Fixed
Rx.Node.fromStream
so that is uses the 'end' event by default to signify the end of a stream, but allows to be overridden. - Created the following toStream shortcuts to support all streams
Rx.Node.fromReadableStream
Rx.Node.fromWritableStream
Rx.Node.fromTransformStream
Rx.Observable.fromEvent
changes
There were a number of changes in how Rx.Observable.fromEvent
worked. We wanted to ensure that fromEvent
was as flexible as possible to utilize the framework you were already using, but also, to use just plain old events just in case.
First off, we added support for Backbone and Backbone Marionette which then brings us up to supporting the following libraries natively:
- Angular.js -
angular.element.on
andangular.element.off
- Backbone and Marionette - binds to
element.addListener
andelement.removeListener
- Ember - binds to
Ember.addListener
andEmber.removeListener
- jQuery - binds to
jQuery.fn.on
andjQuery.fn.off
- Zepto.js - binds to
Zepto.fn.on
andZepto.fn.off
Although we support using these events if they are present, we also offer a way to opt out and only use DOM or Node.js specific events only. By setting the Rx.config.useNativeEvents
flag to true
, this will only use native DOM events or in Node.js if you're using Node.
Rx.config.useNativeEvents = true;
var mousemove = Rx.Observable.fromEvent(document, 'mousemove');
// Note since jQuery unwraps, this is stil supported
var mousemove = Rx.Observable.fromEvent($document, 'mousemove');
Renaming of Rx.Observable.prototype.then
Libraries that use Promises typically do a check to see whether it is a promise by doing the following:
function isPromise(p) {
return p && p.then && typeof p.then === 'function';
}
The problem with that is that RxJS also had a then
method which caused the function to return true, although the RxJS then
has nothing to do with Promises, and instead has to deal with join patterns/join calculus. In order to prevent collision with Promise libraries, we have renamed Rx.Observable.prototype.then
to Rx.Observable.prototype.thenDo
so there should be no more false positives when it comes to checking whether an object is a Promise.
Moving Operators
Another change was moving some operators to a different file to keep the core size of rx.js
smaller. This includes moving Rx.Observable.prototype.groupBy
and Rx.Observable.prototype.groupByUntil
to rx.coincidence.js
to go along with other coincidence operators such as groupJoin
, join
, buffer
and window
.
Changing Method signatures
There were a few changes to some method signatures as well due to feedback from the community and looking at its usage. This shouldn't affect many people as the changes are very slight. In fact, you may not notice them at all.
Callback Changes
The first such change was to remove the schedulers required from Rx.Observable.fromCallback
and Rx.Observable.fromNodeCallback
. The reason is that the concurrency is already handled by the callback, there is no need to introduce another layer of concurrency.
Below is the old version which had schedulers as a parameter.
Rx.Observable.fromCallback = function (func, scheduler, context, selector) {
Rx.Observable.fromNodeCallback = function (func, scheduler, context, selector) {
And here is the updated version which removes the schedulers.
Rx.Observable.fromCallback = function (func, context, selector) {
Rx.Observable.fromNodeCallback = function (func, context, selector) {
Now you can easily use the callback functions such as:
var $elem = $('#elem');
var fadeIn = Rx.Observable.fromCallback($elem.fadeIn, $elem)(1000);
Start and ToAsync Changes
There was another change of ordering of parameters for a couple of methods Rx.Observable.start
and Rx.Observable.toAsync
where it made the API a bit nicer. Before, the scheduler, which was usually set by default, was the second parameter, and was moved to the third parameter as it was purely optional. The context was instead moved to the second parameter as it was more likely to be used.
Below is the old version which had the context third and scheduler second.
Rx.Observable.start = function (func, scheduler, context) {
Rx.Observable.toAsync = function (func, scheduler, context) {
Now the last two parameters have been reversed.
Rx.Observable.start = function (func, context, scheduler) {
Rx.Observable.toAsync = function (func, context, scheduler) {
This allows for much smoother behavior when a context is needed.
function logMessage(msg) {
this.log('Message: %s', msg);
}
var asyncLog = Rx.Observable.toAsync(logMessage, console);
asyncLog('foobar').subscribe();
// => Message: foobar
Conclusion
We hope you enjoy these new and changed features of RxJS. We'd love your feedback by creating issues for us, pull requests and so forth. Documentation, tutorials and other things welcome!