I'm creating an observable and I'm creating the subscription separately:
class CustomQuery {
string Name;
IObservable<int> Occurrences;
}
public IEnumerable<CustomQuery> GatherCustomQueryObservables()
{
yield return new CustomQuery() {
Name = "NameXXX",
Occurrences = Observable.Create<int>(
observer =>
{
int occurrences = this.webservice.GetOccurrences()
observer.OnNext(occurrences);
return System.Reactive.Disposables.Disposable.Empty;
}
);
}
By other hand, there's another method deals with these CustomQueries:
public void CommitCustomQueryObservables(IEnumerable<CustomQuery> queries)
{
foreach (CustomQuery query in queries)
{
query.Occurrences
.Select(o => o)
.SubscribeOn(System.Reactive.Concurrency.TaskPoolScheduler.Default)
.ObserveOn(System.Reactive.Concurrency.DispatcherScheduler.Current)
.Subscribe(
occurrences =>
{
string strOccurrences = occurrences > 0 ? occurrences.ToString() : "";
this.Label.Text = strOccurrences;
}
);
}
}
Nevertheless, I'm getting a System.InvalidOperationException exception:
The current thread has no Dispatcher associated with it.
The last line of the stacktrace is at
System.Reactive.Concurrency.DispatcherScheduler.get_Current().
I don't quite figure out how to handle it.
Any ideas?
For Windows Forms you need to use the ControlScheduler for synchronization, not the DispatcherScheduler.
Now you've added the System.Reactive.Windows.Forms package this can be achieved by simply using [observable].ObserveOn([control]); in your example this could be:
public void CommitCustomQueryObservables(IEnumerable<CustomQuery> queries)
{
foreach (CustomQuery query in queries)
{
query.Occurrences
.Select(o => o)
.SubscribeOn(System.Reactive.Concurrency.TaskPoolScheduler.Default)
.ObserveOn(this.Label)
.Subscribe(
occurrences =>
{
string strOccurrences = occurrences > 0 ? occurrences.ToString() : "";
this.Label.Text = strOccurrences;
}
);
}
}
I have a fn that inherit an existing fn ( take Angular1 $q for example )
//$q original behavior
var defer = $q.defer();
defer.promise.then(function(result){})
//or
$q( (resolve, reject) => {
//promise execution here
}).then(function(result){});
If I want to decorate it, I would do :
var Qdecorator = function($delegate) {
var Q = function(resolver:any): any {
//do some extra stuff here
return $delegate.apply($delegate, arguments);
}
//Assign the static methods here:
Q.defer = function() {
//do some stuff
return $delegate.defer.apply($delegate, []);
}
//same goes for race, when, resole reject and so on
return Q;
}
Problem is that typescript complains about
Property defer, race, when, resolve, etc... does not exist on type '(resolver: any) => any'
I tried to use the IQService, and IPromise with no luck, btu I'd like to raise a more global question :
How do I define late static methods on function() that return an object without using new
I am copying pasting the answer to my question from this link:
https://www.typescriptlang.org/docs/handbook/interfaces.html
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
I have this code
it('This should pass anyway', function (done) {
testObj.testIt(regStr);
});
testObj
this.testIt = function (regStr) {
selector.count().then(function (orgCount) {
for (var curr = 0; curr < count; curr++) {
checkField(curr, regStr);
}
});
};
function checkField(curr, regStr) {
selector.get(curr).all(by.tagName('li')).get(0).getInnerHtml().then(function (text) {
expect(text).to.match(regStr, curr + '#ERR');
});
}
If one of these expects get a failure, test fails. How can i handle this? I mean - can i somehow count passed and failed expect()ations and return it? or, at least, dont let test break on first error.
I've tried try-catch, but nothing good happened.
it('This should pass anyway', function (done) {
try {
testObj.testIt(regStr);
} catch (e) {
console.log('#err' + e);
}
});
And then i wanted to use done(), but havent found any examples to do the similar. Can u please help me?
Sry for my english
UPD
You can return either null or a string from checkField(), join them up, and expect the array to be empty:
this.testIt = function (regStr) {
selector.count().then(function (orgCount) {
var errors = [];
for (var curr = 0; curr < orgCount; curr++) {
var e = checkField(curr, regStr);
if (e) { errors.push(e); }
}
assert.equal(0, errors.length, errors);
});
};
A cleaner approach would be to use map() to collect the data into an array:
var data = selector.map(function (elm) {
return elm.element(by.tagName('li')).getText();
});
expect(data).toEqual(["test1", "test2", "test3"]);
With Rx, what is the best way to get the number of current observers in a Subject?
I have a scenario where I want to publish a message, but only if there are observers. If there are no observers, I need to do something else.
To get around this issue, what I've done is created my own ISubject implementation and expose a count of an internal IObserver collection. I'm sure there must be an out of the box way of doing this, I'm just not fully familiar with what Rx has to offer.
Thanks!
Use the Subject<T>.HasObservers property.
Source Code
I don't recall exactly when it was introduced, but I'm pretty sure that it wasn't always there. It was probably added in Rx 2.0.
You should avoid implementing your own observable (or subject) implementations whenever possible.
You could certainly try writing a wrapper class to help.
Try this:
public class Countable
{
private int _count;
public int Count { get { return _count; } }
public IObservable<T> GetCountable<T>(IObservable<T> source)
{
return Observable.Create<T>(o =>
{
Interlocked.Increment(ref _count);
var subscription = source.Subscribe(o);
var decrement = Disposable.Create(() =>
{
Interlocked.Decrement(ref _count);
});
return new CompositeDisposable(subscription, decrement);
});
}
}
You can then write code like this:
var xs = new Subject<int>();
var countable = new Countable();
var ys = countable.GetCountable(xs);
Console.WriteLine(countable.Count);
var s1 = ys.Subscribe(y => { });
Console.WriteLine(countable.Count);
var s2 = ys.Subscribe(y => { });
Console.WriteLine(countable.Count);
s1.Dispose();
Console.WriteLine(countable.Count);
s2.Dispose();
Console.WriteLine(countable.Count);
My results running this are:
0
1
2
1
0
use subject.observers.length, example:
import {Subject} from 'rxjs'
let subject = new Subject()
let s1 = subject.subscribe(v => console.log('observerA: ' + v))
subject.next(1) // observerA: 1
console.log(subject.observers.length) // 1
let s2 = subject.subscribe(v => {
console.log('observerB: ' + v)
if(v===3) s2.unsubscribe()
})
subject.next(2) // observerA: 2
console.log(subject.observers.length) // 2
subject.next(3) // observerA: 3
console.log(subject.observers.length) // 1
I need to implement a version of CombineLatest (I'll call it WithLatest here) that calls the selector for every item on the left and the latest item on the right. It shouldn't push for items on the right changing only.
I think whether this is built Observable.Create or a combination of existing extensions is not particularly important; I'll be making this a "boxed" extension method either way.
Example
var left = new Subject<int>();
var right = new Subject<int>();
left.WithLatest(right, (l,r) => l + " " + r).Dump();
left.OnNext(1); // <1>
left.OnNext(2); // <2>
right.OnNext(1); // <3>
right.OnNext(2); // <4>
left.OnNext(3); // <5>
should yield
2 1
3 2
Edit: The logic of my example goes:
Left becomes populated with 1. Right is empty, no values pushed.
Left becomes updated with 2 (it forgets the previous value). Right is still empty, so nothing is pushed.
Right becomes populated with 1, so Left = 2 (the latest value), Right = 1 is pushed. Up to this point, there is no difference between WithLatest and CombineLatest
Right is updated -- nothing is pushed. This is what's different
Left is updated with 3, so Left = 3, Right = 2 (the latest value) is pushed.
It's been suggested that I try:
var lr = right.ObserveOn(Scheduler.TaskPool).Latest();
left.Select(l => l + " " + lr.First()).Dump();
but this blocks on the current thread for my test.
You can do this using existing operators.
Func<int, int, string> selector = (l, r) => l + " " + r;
var query = right.Publish(rs => left.Zip(rs.MostRecent(0), selector).SkipUntil(rs));
Publish ensures we only ever subscribe to right once and share the subscription among all subscribers to rs.
MostRecent turns an IObservable<T> into an IEnumerable<T> that always yields the most recently emitted value from the source observable.
Zip between IObservable<T> and IEnumerable<U> emits a value each time the observable emits a value.
SkipUntil skips the pairs (l, r) which occur before right ever emits a value.
I also had the same need for a CombineLatest which "pushes only for the left".
I made the solution an "overload" of Observable.Sample, because that's what the method does:
It samples a source (right) with a sampler (left), with the additional capability of providing a resultSelector (like in CombineLatest).
public static IObservable<TResult> Sample<TSource, TSample, TResult>(
this IObservable<TSource> source,
IObservable<TSample> sampler,
Func<TSource, TSample, TResult> resultSelector)
{
var multiSampler = sampler.Publish().RefCount();
return source.CombineLatest(multiSampler, resultSelector).Sample(multiSampler);
}
Based on the solution picked by the post author I think there's an even simpler solution utilizing DistinctUntilChanged:
public static IObservable<TResult> CombineLatestOnLeft<TLeft, TRight, TResult>(this IObservable<TLeft> leftSource, IObservable<TRight> rightSource, Func<TLeft, TRight, TResult> selector) {
return leftSource
.Select<TLeft, Tuple<TLeft, int>>(Tuple.Create<TLeft, int>)
.CombineLatest(rightSource,
(l, r) => new { Index = l.Item2, Left = l.Item1, Right = r })
.DistinctUntilChanged(x => x.Index)
.Select(x => selector(x.Left, x.Right));
}
or even
public static IObservable<TResult> CombineLatestOnLeft<TLeft, TRight, TResult>(this IObservable<TLeft> leftSource, IObservable<TRight> rightSource, Func<TLeft, TRight, TResult> selector) {
return leftSource
.CombineLatest(rightSource,
(l, r) => new { Left = l, Right = r })
.DistinctUntilChanged(x => x.Left)
.Select(x => selector(x.Left, x.Right));
}
if you only care about distinct values of leftSource
On latest System.Reactive, we can use WithLatestFrom extension method.
left.WithLatestFrom(right, (l, r) => l + " " + r).Dump();
The result would be below correctly.
3 2
Here's the hacky way using Create - didn't actually build it, mea culpa if it doesn't actually work :)
public static IObservable<TRet> WithLatest<TLeft, TRight, TRet>(
this IObservable<TLeft> lhs,
IObservable<TRight> rhs,
Func<TLeft, TRight, TRet> sel)
{
return Observable.Create<TRet>(subj => {
bool rhsSet = false;
bool deaded = false;
var latestRhs = default(TRight);
Action onDeaded = null;
var rhsDisp = rhs.Subscribe(
x => { latestRhs = x; rhsSet = true; },
ex => { subj.OnError(ex); onDeaded(); });
var lhsDisp = lhs
.Where(_ => deaded == false && rhsSet == true)
.Subscribe(
x => subj.OnNext(sel(x, latestRhs)),
ex => { subj.OnError(ex); onDeaded(); },
() => { subj.OnCompleted(); onDeaded(); });
onDeaded = () => {
deaded = true;
if (lhsDisp != null) {
lhsDisp.Dispose();
lhsDisp = null;
}
if (rhsDisp != null) {
rhsDisp.Dispose();
rhsDisp = null;
}
};
return onDeaded;
});
}
I made a RX operator for project today that does this.
Here's my solutions:
public static IObservable<Tuple<TSource, TTarget>> JoinLeftSoft<TSource, TTarget>(
this IObservable<TSource> source, IObservable<TTarget> right)
{
return source
.Select(x => new Tuple<object, TSource>(new object(), x))
.CombineLatest(right, (l, r) => new Tuple<object, TSource, TTarget>(l.Item1, l.Item2, r))
.DistinctUntilChanged(t => t.Item1)
.Select(t => new Tuple<TSource, TTarget>(t.Item2, t.Item3));
}