Releases: Reactive-Extensions/RxJS
RxJS version 2.3.22
This is a significant update from the previous supported release of v2.3.18 which contains a number of new pieces.
The major items in this release are:
- Long Stack Traces Support
- Tessel Support
- More Backpressure Operators
- TypeScript Definitions Updates
- Documentation and Code Updates
- More Examples
Long Stack Traces Support
When dealing with large RxJS applications, debugging and finding where the error occurred can be a difficult operation. As you chain more and more operators together, the longer the stack trace gets, and the harder it is to find out where things went wrong. This has been a request from the community for quite some time, so now your wish is our command. We took inspiration from the Q
library from @kriskowal which helped us get started.
RxJS now comes with optional support for “long stack traces,” wherein the stack property of Error from onError
calls is rewritten to be traced along asynchronous jumps instead of stopping at the most recent one. As an example:
var Rx = require('rx');
var source = Rx.Observable.range(0, 100)
.timestamp()
.map(function (x) {
if (x.value > 98) throw new Error();
return x;
});
source.subscribeOnError(
function (err) {
console.log(err.stack);
});
The error stack easily becomes unreadable and hard to find where the error actually occurred:
$ node example.js
Error
at C:\GitHub\example.js:6:29
at AnonymousObserver._onNext (C:\GitHub\rxjs\dist\rx.all.js:4013:31)
at AnonymousObserver.Rx.AnonymousObserver.AnonymousObserver.next (C:\GitHub\rxjs\dist\rx.all.js:1863:12)
at AnonymousObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (C:\GitHub\rxjs\dist\rx.all.js:1795:35)
at AutoDetachObserverPrototype.next (C:\GitHub\rxjs\dist\rx.all.js:9226:23)
at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (C:\GitHub\rxjs\dist\rx.all.js:1795:35)
at AnonymousObserver._onNext (C:\GitHub\rxjs\dist\rx.all.js:4018:18)
at AnonymousObserver.Rx.AnonymousObserver.AnonymousObserver.next (C:\GitHub\rxjs\dist\rx.all.js:1863:12)
at AnonymousObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (C:\GitHub\rxjs\dist\rx.all.js:1795:35)
at AutoDetachObserverPrototype.next (C:\GitHub\rxjs\dist\rx.all.js:9226:23)
Instead, we can turn on this feature by setting the following flag:
Rx.config.longStackSupport = true;
When running the same example again with the flag set at the top, our stack trace looks much nicer and indicates exactly where the error occurred:
$ node example.js
Error
at C:\GitHub\example.js:6:29
From previous event:
at Object.<anonymous> (C:\GitHub\example.js:3:28)
From previous event:
at Object.<anonymous> (C:\GitHub\example.js:4:4)
From previous event:
at Object.<anonymous> (C:\GitHub\example.js:5:4)
As you'll see our error did occur exactly at line 6 with throwing an error and only shows the user code in this point. This is very helpful for debugging, as otherwise you end up getting only the first line, plus a bunch of RxJS internals, with no sign of where the operation started.
This feature does come with a serious performance and memory overhead, however, If you're working with lots of RxJS code, or trying to scale a server to many users, you should probably keep it off. In development, this is perfectly fine for finding those pesky errors!
In a future release, we may also release support for a node.js environment variable so that you can set it and unset it fairly easily.
Tessel Support
RxJS has a mission to be supported on every JavaScript platform, ranging from node.js, to the browser, in the OS runtime, and even directly on hardware. To that point, we aimed to get RxJS supported on the Tessel board. There were several issues with RxJS that had to be addressed due to the following Tessel Runtime issues.
- Issue 682: ES3 Object#propertyIsEnumerable missing
- Issue 658: Lua has a hard limit on 200 local variables
To overcome Issue 682, we implemented a naive version of Object.prototype.propertyIsEnumerable
if it is missing. This also will overcome a bug in Safari 2 as well. In order to solve Issue 658, you can only use rx.lite
or rx.lite.compat
directly with the Tessel at this time of the release.
We now ship the blinky example for the Tessel using RxJS in our examples folder.
// Run with "tessel run blinky.js"
// Import the interface to Tessel hardware
var tessel = require('tessel');
// Must use lite compat due to https://github.com/tessel/runtime/issues/658
var Observable = require('../../dist/rx.lite').Observable;
// Set the led pins as outputs with initial states
// Truthy initial state sets the pin high
// Falsy sets it low.
var led1 = tessel.led[0].output(1);
var led2 = tessel.led[1].output(0);
Observable.interval(100)
.subscribe(function () {
console.log('first blinks');
led1.toggle();
});
Observable.interval(150)
.subscribe(function () {
console.log('second blinks');
led2.toggle();
});
We hope those issues get resolved soon so that you can use the entire RxJS. If you wish, you could also create a custom build of RxJS as well with fewer methods which would solve the issues.
More Backpressure Operators
In previous releases of RxJS, we have implemented some basic backpressure operators, whether lossy or lossless through pausable
, pausableBuffered
and controlled
. In this release we took the idea of the controlled
operator even further with two further specializations with "Stop and Wait" and "Sliding Window". Much of this work is derived work from Johann Laanstra in his BackPressureRx project.
Stop-and-wait
The most simple flow control algorithm is stop-and-wait. It works by simply producing a value and wait until is has been processed completely. Stop-and-wait is a special case of the Sliding Window protocol, which will be discussed later, where the window has size.
In the implementations of these flow control algortihms for RxJS we don’t have to take into account acknowledgments and damage of values, This allows us to simplify stop-and-wait by removing the need for an Automatic Repeat reQuest (ARQ) mechanism and CRC checks on the values.
works by default similar to stop-and-wait. The onNext
call blocks until the value has been processed by the chain or until the value gets scheduled on a different scheduler. If there or no switches between scheduler in a chain, this means there will always be a single value in the chain at any moment. Using the controlled
operator we can use the increased flexibility to also make he case with different schedulers work, by requesting only one value at a time.
Stop-and-wait has been implemented as the stopAndWait
combinator on a ControlledObservable
. It represents the most basic synchronous case, where a thread starts requesting items and those items are pushed on that same thread.
StopAndWaitObserver.prototype.next = function (value) {
this.observer.onNext(value);
var self = this;
Rx.Scheduler.timeout.schedule(function () {
self.observable.source.request(1);
});
};
When an observer subscribes to the stopAndWait
, it subscribes to the underlying ControlledObservable
and attaches itself as the controller. It starts by requesting a single value and then every time a value has come to its onNext
method, it asynchronously requests a new value. Multiple subscribers can be attached but there will only be a single controller active. The logic to do this can be seen in the subscribe
method. As soon as all subscribers have been detached, the controller will also be detached. As you can see in the above code the observer is wrapped in a StopAndWaitObserver
class. This class request the new value as soon as the current one has been processed by the queue, which can be seen in the next
method.
An example of this in action is the following:
var source = Rx.Observable.interval(1000)
.timestamp()
.controlled();
source.stopAndWait().subscribe(function (x) {
// Do something with the value
});
Sliding Window Protocol
A more advanced flow control algorithm is a Sliding Window. Sliding Window is a method of flow control in which a receiver gives a transmitter permission to transmit data until a window is full. When the window is full, the transmitter must stop transmitting until the receiver advertises a larger window. Sliding Window is the protocol used by TCP to do flow control. For the Sliding Window protocol we can perform the same optimizations as for the Stop-and-Wait protocol. RxJS does not have to take care of damaged or lost values.
We on the Rx team created a sliding window combinator based on the code for stopAndWait
called windowed
. RxJS wants to request a new value as soon as there is a slot available in the window, but instead of requesting a single value to begin with, RxJS requests multiple at the start, and thereby defining the window size that will be used by the observable. By using windowed(1)
, will gives us the stop-and-wait algorithm, which now could be simply implemented using the windowed
operator.
WindowedObserver.prototype.next = function (value) {
this.observer.onNext(value);
this.received = ++this.received % this.observable.window...
RxJS version 2.3.18
This is a minor update from version 2.3.14. This release will probably be one of the last in the version 2.3 series before we start on the more ambitious plans for 2.4 including mirroring the lift
based approach of RxJava, as well as backpressure built into the subscription level instead of the operator level, as well as plans for expression tree parsing.
In coming releases, we are going to try and get rid of some of our technical debt including some of our poorly named operators such as throttle
which we discuss below as well as other ones such as fromArray
instead of using from
.
The major items for this release are:
- Backpressure Fixes
- Debounce and Throttle Changes
- Scheduler Changes
- Testing with Promises
- JSCS and Google Coding Standards Enforcement
- Improved Documentation
Backpressure Fixes
There was an issue with a prior release of the pausableBuffered
operator which was noted in issue #349 where if the stream ended before the stream resumed, data would have been automatically pushed anyhow. In addition, there were issues where errors were immediately processed regardless of whether the stream was in a paused state. Those two bugs have now been fixed and can be verified with these test cases.
Success Case
var scheduler = new TestScheduler();
var results = scheduler.createObserver();
var xs = scheduler.createHotObservable(
onNext(150, 1),
onNext(210, 2),
onNext(230, 3),
onNext(301, 4),
onNext(350, 5),
onNext(399, 6),
onNext(450, 7),
onNext(470, 8),
onCompleted(500)
);
var controller = scheduler.createHotObservable(
onNext(201, true),
onNext(300, false),
onNext(600, true)
);
var results = scheduler.startWithCreate(function () {
return xs.pausableBuffered(controller);
});
results.messages.assertEqual(
onNext(210, 2),
onNext(230, 3),
onNext(600, 4),
onNext(600, 5),
onNext(600, 6),
onNext(600, 7),
onNext(600, 8),
onCompleted(600)
);
Error Case
var error = new Error();
var scheduler = new TestScheduler();
var results = scheduler.createObserver();
var xs = scheduler.createHotObservable(
onNext(150, 1),
onNext(210, 2),
onNext(230, 3),
onNext(301, 4),
onNext(350, 5),
onNext(399, 6),
onNext(450, 7),
onNext(470, 8),
onError(500, error)
);
var controller = scheduler.createHotObservable(
onNext(201, true),
onNext(300, false),
onNext(600, true)
);
var results = scheduler.startWithCreate(function () {
return xs.pausableBuffered(controller);
});
results.messages.assertEqual(
onNext(210, 2),
onNext(230, 3),
onNext(600, 4),
onNext(600, 5),
onNext(600, 6),
onNext(600, 7),
onNext(600, 8),
onError(600, error)
);
As you will note, the values are yielded after the sequence had already ended and instead were yielded when the controller resumed.
Debounce and Throttle Changes
There has been a bit of confusion around some of the operators in RxJS such as throttle
. Our version of throttle
acted instead of a throttling operator, it acted more as a debounce
operator. RxJava, while designing their APIs came across this inconsistency This was noted in issue #352 where we would deprecate throttle
in favor of debounce
or throttleWithTimeout
. Note that in upcoming versions, warnings will occur to stop using throttle
. We also introduced throttleFirst
which acts as a proper throttle is expected to do. Thanks to @L8D for the help!
Scheduler Changes
Node.js Recently made some changes in the 10.x release where warnings would occur if the use of process.nextTick
were used for a recursive function call. This caused issues in any number of our time-based operators as noted in issue #344. To fix this, we simply changed the order to use setImmediate
if available in Node.js, else default back to process.nextTick
for older versions in the Rx.Scheduler.timeout
.
Testing with Promises
RxJS prides itself on deterministic testing with virtual time. As RxJS has evolved, we have added Promise support to many of our operators as noted in our Bridging to Promises documentation. This was addressed in issue #331 where you have the ability to create both a resolved and rejected Promise which is synchronous and records in virtual time via the createResolvedPromise
and createRejectedPromise
on the Rx.TestScheduler
class.
Now you can test using these Promises for exact timing information!
Resolved Promise
var scheduler = new TestScheduler();
var xs = scheduler.createResolvedPromise(201, 1);
var results = scheduler.startWithCreate(function () {
return Observable.fromPromise(xs);
});
results.messages.assertEqual(
onNext(201, 1),
onCompleted(201)
);
Rejected Promise
var error = new Error();
var scheduler = new TestScheduler();
var xs = scheduler.createRejectedPromise(201, error);
var results = scheduler.startWithCreate(function () {
return Observable.fromPromise(xs);
});
results.messages.assertEqual(
onError(201, error)
);
This now makes testing when using Promises in your API a snap!
JSCS and Google Coding Standards Enforcement
RxJS has fairly rigid coding guidelines as outlined in our Design Guidelines and our Contribution Guidelines. To enforce this, we have added JSCS to our Grunt-based build system to include the rules that Google uses for their JavaScript Style Guide as noted in issue #359. Thanks to @hzoo for making this happen.
Improved Documentation
Documentation has been one of the biggest focus areas for learning RxJS. To that end, we've added a number of documents that are helpful in learning RxJS including:
- How we build RxJS
- Which Operator should I use?
- Getting to know RxJS
Many thanks to @trxcllnt for the help on the which operators to use.
RxJS version 2.3.14
This release of RxJS is a minor release which followed 2.3.13, which was only a fix for Rx.spawn
to ensure it was thoroughly tested.
This release contained the following:
- Transducers Support
- Adding Iterable Support to
flatMap
/seleectMany
andconcatMap
/selectConcat
Transducers Support
Much like Language Integrated Query (LINQ), Transducers are composable algorithmic transformations. They, like LINQ, are independent from the context of their input and output sources and specify only the essence of the transformation in terms of an individual element. Because transducers are decoupled from input or output sources, they can be used in many different processes - collections, streams, observables, etc. Transducers compose directly, without awareness of input or creation of intermediate aggregates. There are two major libraries currently out there, Cognitect's transduce.js
and James Long's transduce-js
which are both great for getting high performance over large amounts of data. Because it is collection type neutral, it is a perfect fit for RxJS to do transformations over large collections.
The word transduce
is just a combination of transform
and reduce
. The reduce function is the base transformation; any other transformation can be expressed in terms of it (map
, filter
, etc).
var arr = [1, 2, 3, 4];
arr.reduce(
function(result, x) { return result.concat(x + 1); }, []);
// => [ 2, 3, 4, 5 ]
Using transducers, we can model the following behavior while breaking apart the map aspect of adding 1 to the concat operation, adding the seed and then the "collection" to transduce.
var arr = [1, 2, 3, 4];
function increment(x) { return x + 1; }
function concatItem(acc, x) { return acc.concat(x); }
transduce(map(increment), concatItem, [], arr);
// => [ 2, 3, 4, 5 ]
Using Cognitect's transduce.js
library, we can easily accomplish what we had above.
var t = transducers;
var arr = [1, 2, 3, 4];
function increment(x) { return x + 1; }
into([], t.comp(t.map(increment)), arr);
// => [ 2, 3, 4, 5 ]
We can go a step further and add filtering as well to get only even values.
var t = transducers;
var arr = [1, 2, 3, 4];
function increment(x) { return x + 1; }
function isEven(x) { return x % 2 === 0; }
into([], t.comp(t.map(increment), t.filter(isEven)), arr);
// => [ 2, 4 ]
Since it works so well using Arrays, there's no reason why it cannot work for Observable sequences as well. To that end, we have introduced the transduce
method which acts exactly like it does for Arrays, but for Observable sequences. Once again, let's go over the above example, this time using an Observable sequence.
var t = transducers;
var source = Rx.Observable.range(1, 4);
function increment(x) { return x + 1; }
function isEven(x) { return x % 2 === 0; }
var transduced = source.transduce(t.comp(t.map(increment), t.filter(isEven)));
transduced.subscribe(
function (x) { console.log('Next: %s', x); },
function (e) { console.log('Error: %s', e); },
function () { console.log('Completed'); });
// => Next: 2
// => Next: 4
// => Completed
Note that this above example also works the same with transducers.js
as well with little to no modification. This example will in fact work faster than the traditional LINQ style (as of now) which most use currently.
var source = Rx.Observable.range(1, 4);
function increment(x) { return x + 1; }
function isEven(x) { return x % 2 === 0; }
var transduced = source.map(increment).filter(isEven);
transduced.subscribe(
function (x) { console.log('Next: %s', x); },
function (e) { console.log('Error: %s', e); },
function () { console.log('Completed'); });
// => Next: 2
// => Next: 4
// => Completed
This opens up a wide new set of possibilities making RxJS even faster over large collections with no intermediate Observable sequences.
Iterable Support for flatMap
/seleectMany
and concatMap
/selectConcat
In order to maintain parity with the Reactive Extensions for .NET, RxJS has now added support for Arrays and ES6 iterables (Map, Set, Generator). So now, it is possible to do the following:
/* Using an array */
Rx.Observable.of(1,2,3)
.flatMap(
function (x, i) { return [x,i]; },
function (x, y, ix, iy) { return x + y + ix + iy; }
);
var subscription = source.subscribe(
function (x) {
console.log('Next: ' + x);
},
function (err) {
console.log('Error: ' + err);
},
function () {
console.log('Completed');
});
// => Next: 2
// => Next: 2
// => Next: 5
// => Next: 5
// => Next: 8
// => Next: 8
// => Completed
We can also have that for a Set as well:
/* Using an array */
Rx.Observable.of(1,2,3)
.flatMap(
function (x, i) { return new Set[x,i]; },
function (x, y, ix, iy) { return x + y + ix + iy; }
);
var subscription = source.subscribe(
function (x) {
console.log('Next: ' + x);
},
function (err) {
console.log('Error: ' + err);
},
function () {
console.log('Completed');
});
// => Next: 2
// => Next: 2
// => Next: 5
// => Next: 5
// => Next: 8
// => Next: 8
// => Completed
And finally, we could also have it for an ES6 generator as well:
/* Using an array */
Rx.Observable.of(1,2,3)
.flatMap(
function (x, i) { return function* () { yield x; yield i; }(); },,
function (x, y, ix, iy) { return x + y + ix + iy; }
);
var subscription = source.subscribe(
function (x) {
console.log('Next: ' + x);
},
function (err) {
console.log('Error: ' + err);
},
function () {
console.log('Completed');
});
// => Next: 2
// => Next: 2
// => Next: 5
// => Next: 5
// => Next: 8
// => Next: 8
// => Completed
RxJS Version 2.3
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 t...
Reactive Extensions for JavaScript (RxJS) version 2.2.28
Minor update from previous versions to include the following:
New Features
- Added
Rx.Observable.of
to matchArray.of
Rx.Observable.of
To match the semantics of ES6 Arrays, we have added Rx.Observable.of
and Rx.Observable.ofWithScheduler
to match the ES6 proposal of Array.of
. The latter is required for scheduling purposes if you wish to swap a scheduler for the default scheduler.
Below is an example of its usage:
var source = Rx.Observable.of(1,2,3);
var subscription = source.subscribe(
function (x) {
console.log('Next: ' + x);
},
function (err) {
console.log('Error: ' + err);
},
function () {
console.log('Completed');
});
// => Next: 1
// => Next: 2
// => Next: 3
// => Completed
Bug Fixes
Reactive Extensions v2.2.25
This is a minor update from RxJS version 2.2.24 which includes the following changes including new operators of concatMap
or its alias selectConcat
ConcatMap/SelectConcat
A new operator was introduced to help chain together serial asynchronous operations and preserve order called concatMap
or its alias selectConcat
. This operator in its intent is nothing more than map(...).concat()
. In contrast with flatMap
or its alias selectMany
, is nothing more than map(...).merge()
where order is not preserved in order, but instead as to the time that the observable responds.
Below is an example of its usage.
var source = Rx.Observable.range(0, 5)
.concatMap(function (x, i) {
return Rx.Observable
.interval(100)
.take(x).map(function() { return i; });
});
var subscription = source.subscribe(
function (x) {
console.log('Next: ' + x);
},
function (err) {
console.log('Error: ' + err);
},
function () {
console.log('Completed');
});
// => Next: 1
// => Next: 2
// => Next: 2
// => Next: 3
// => Next: 3
// => Next: 3
// => Next: 4
// => Next: 4
// => Next: 4
// => Next: 4
// => Completed
Also, note that just like flatMap
and selectMany
, these methods also accept JavaScript Promises as inputs.
var source = Rx.Observable.fromArray([1,2,3,4])
.concatMap(function (x, i) {
return Promise.resolve(x + i);
});
var subscription = source.subscribe(
function (x) {
console.log('Next: ' + x);
},
function (err) {
console.log('Error: ' + err);
},
function () {
console.log('Completed');
});
// => Next: 4
// => Next: 4
// => Next: 4
// => Next: 4
// => Completed
Closed Issues
Reactive Extensions v2.2.24
This is another minor release of the Reactive Extensions for JavaScript which contains mostly bug fixes as we move towards version 2.3. This release includes some great stuff including a complete build (minus the testing), RxJS Lite Extras, and some fixes to behaviors such as with finally
.
RxJS Complete Build
One feature that was requested often was to have a complete build of all RxJS operators in a single file, so that you don't have to hunt down which file contains the desired operator. This is good for prototyping where you are undecided about which operators you are going to use. Later on, you may decide you may need only a subset which might be only in rx.lite.js
. The only parts that are not included in this file are for testing, which you can easily reference yourself.
To that end, we now ship rx.all.js
for modern browsers and rx.all.compat.js
for older browsers.
RxJS Lite Extras
Another requested feature was for the ability to include the operators that were included in the main rx.js
file, but not included in rx.lite.js
. We have created rx.lite.extras.js
which contains the missing operators, which makes including other files such as rx.joinpatterns.js
, rx.coincidence.js
or others easier without worrying if something is missing.
The following operators included are:
Observable Methods
Observable Instance Methods
amb
bufferWithCount
distinct
groupBy
groupByUntil
observeOn
onErrorResumeNext
subscribeOn
windowWithCount
Finally Behavior Fix
An issue was brought to our attention that if the subscribe is empty and an exception is thrown, the finally
will not be called because there was no try/catch around the subscribe
call. Below was the previous behavior which would not call the finally
.
Rx.Observable.throw(new Error())
.finally(function () { /* never would be hit */ })
.subscribe();
// Error thrown
This has been corrected so we now get the expected behavior:
Rx.Observable.throw(new Error())
.finally(function () { console.log('finally'); })
.subscribe();
// => finally
// Error thrown
Removal of rxjs Node.js Module
A point of confusion was the existence of the rxjs
Node.js module, which confused many as the official supported module is rx
. We have since contact the author and it has been removed.
Closed Issues
- #168: Error: Cannot find module './rx.all' on Node.js
- #167: Fix data binding example
- #166: rx.js throws exception when used in web worker
- #165: Update observable.md
- #164: fix broken anchor links: shareValue/shareReplay
- #163: fix broken anchor link on concatAll
- #162: correct broken anchor link to mergeAll
- #161: correct pausableBuffered docs
- #160: s/teh/the/
- #159: correct typo in
last
docs - #158: correct typo in lastOrDefault docs
- #156: warn against 'npm install rxjs' 3rd party version
- #154: Move compiled output to dist to clean up main folder
- #153: Create extras build for lite
- #152: Create all build
- #151 Add an asyncMap that will map an async operator over an Observable
- #147: Corrected minor spelling mistakes.
- #146: finallyAction does not happen when there is an error
RxJS Release v2.2.20
Slight update to include bug fixes from RxJS v2.2.19 and adding more Promises support
Promises Support
The sequenceEqual
operator now supports promises via 21ecc90. You can now specify a Promise as the other value such as the following:
var sequence = Rx.Observable.return(42);
var promise = new RSVP.Promise(function (res, rej) { res(42); });
var equal = sequence.sequenceEqual(promise);
var subscription = equal.subscribe(console.log.bind(console));
// => true
Bugs Closed
RxJS Release v2.2.19
This is a bit of an update from v2.2.18 with a couple of changes including:
- Rx.Observable.fromEvent Changes
- Backpressure Changes
Note that there are no breaking changes in this release.
Rx.Observable.fromEvent Changes
RxJS at its core will now support a number of libraries to help developers be productive without having to bring in other library specific RxJS bindings such as RxJS-jQuery. It will now support the following libraries
- jQuery
- Zepto
- AngularJS
- Ember
RxJS will now shortcut to use native methods for jQuery, Zepto, Angular's jqLite, and Ember, thus giving you the same experience had you used the library directly. The only caveat is that this method does not support a selector
string which jQuery, Zepto and Ember support.
Backpressure Changes
In previous releases, the mechanism to pause and resume on both the pausable
and pausableBuffered
methods was to use the Subject directly to pass in either true
or false
depending on whether you wanted to pause or resume. In this release, this has been simplified to be able to use pause
and resume
to make it much more intuitive.
var pauser = new Rx.Subject();
// Create pausable
var pausable = Rx.Observable.fromEvent(document, 'mousemove').pausable(pauser);
var subscription = pausable.subscribe(function (next) {
// Do something with values
});
// Now pause/resume
// unpause
pausable.resume();
// Stop and resume every five seconds
// Note: can use the outside Subject still too, no breaking changes.
var shouldRun = true;
setInterval(function () {
if (shouldRun = !shouldRun) {
pausable.pause();
} else {
pausable.resume();
}
}, 5000);
In addition, the requirement for an external Rx.Subject
has been made optional, so that the subject can stay internal and therefore not shared.
// With no outside controller
var pausable = Rx.Observable.fromEvent(document, 'mousemove').pausable();
var subscription = pausable.subscribe(function (next) {
// Do something with values
});
RxJS Release v2.2.18
Update from RxJS v2.2.17 to include the following changes.
Helper Functions
In order to facilitate people using default parameters, RxJS exports helper functions in the Rx.helpers
object including:
noop
- No operationidentity
- Passthrough function which returns the parameter value.defaultNow
- Default function for getting the current time.defaultComparer
- Default comparer which uses the intervalRx.internals.isEqual
function.defaultSubComparer
- Default comparer for using comparisons of greater than, less than or equal to.defaultKeySerializer
Default key serializer function which calls.toString
by default.defaultError
- Throws the given errorisPromise
- Used to detect whether the given object is a Promise.asArray
- Converts the arguments of the function into an array.not
- Returns the opposite of the current function's return value.
More Promises Support
In addition to the previous support for Promises from the previous release for flatMap
/selectMany
,we have now extended it to the following:
Rx.Observable.catch
Rx.Observable.concat
Rx.Observable.defer
An example for defer would look like the following:
// Concat on Promises to execute sequentially
var promises = [ur1, url2, url3].map(function (url) {
// Ensure we don't fire each until it is subscribed
return Rx.Observable.defer(function () {
return getJSON(url);
});
});
var sources = Rx.Observable.concat(promises).toArray();
sources.subscribe(function (results) {
// Get all the results as an array
});
In addition, this works with concat
such as the following:
/* Concat as arguments */
var sources = Rx.Observable.concat(promise1, promise2, promise3);
/* Concat as an array */
var sources = Rx.Observable.concat([promise1, promise2, promise3]);
/* Concat on an Observable */
var newSource = source.concat(promise1);
In addition, this works nicely with catch
as well.
/* Catch as arguments */
var sources = Rx.Observable.catch(promise1, promise2, promise3);
/* Catch as an array */
var sources = Rx.Observable.catch([promise1, promise2, promise3]);
/* Catch on an Observable */
var newSource = source.catch(promise1);
As time goes on, we'll also be looking at other areas for support for promises within RxJS.