This is based on the code presented in this SO : Write an Rx "RetryAfter" extension method
I am using the code by Markus Olsson (evaluation only at the mom
For anyone else that happens upon this post, it was indeed fixed by suggestions made by James World, and Brandon (thanks chaps).
Here is full working code
class Attempt1
{
private static bool shouldThrow = true;
static void Main(string[] args)
{
Generate().RetryWithBackoffStrategy(3, MyRxExtensions.ExponentialBackoff,
ex =>
{
return ex is NullReferenceException;
}, Scheduler.TaskPool)
.Subscribe(
OnNext,
OnError
);
Console.ReadLine();
}
private static void OnNext(int val)
{
Console.WriteLine("subscriber value is {0} which was seen on threadId:{1}",
val, Thread.CurrentThread.ManagedThreadId);
}
private static void OnError(Exception ex)
{
Console.WriteLine("subscriber bad {0}, which was seen on threadId:{1}",
ex.GetType(),
Thread.CurrentThread.ManagedThreadId);
}
static IObservable Generate()
{
return Observable.Create(
o =>
{
Scheduler.TaskPool.Schedule(() =>
{
if (shouldThrow)
{
shouldThrow = false;
Console.WriteLine("ON ERROR NullReferenceException");
o.OnError(new NullReferenceException("Throwing"));
}
Console.WriteLine("Invoked on threadId:{0}",
Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("On nexting 1");
o.OnNext(1);
Console.WriteLine("On nexting 2");
o.OnNext(2);
Console.WriteLine("On nexting 3");
o.OnNext(3);
o.OnCompleted();
Console.WriteLine("On complete");
Console.WriteLine("Finished on threadId:{0}",
Thread.CurrentThread.ManagedThreadId);
});
return () => { };
});
}
}
public static class MyRxExtensions
{
///
/// An exponential back off strategy which starts with 1 second and then 4, 9, 16...
///
public static readonly Func ExponentialBackoff = n => TimeSpan.FromSeconds(Math.Pow(n, 2));
public static IObservable RetryWithBackoffStrategy(
this IObservable source,
int retryCount = 3,
Func strategy = null,
Func retryOnError = null,
IScheduler scheduler = null)
{
strategy = strategy ?? MyRxExtensions.ExponentialBackoff;
int attempt = 0;
return Observable.Defer(() =>
{
return ((++attempt == 1) ? source : source.DelaySubscription(strategy(attempt - 1), scheduler))
.Select(item => new Tuple(true, item, null))
.Catch, Exception>(e =>
retryOnError(e)
? Observable.Throw>(e)
: Observable.Return(new Tuple(false, default(T), e)));
})
.Retry(retryCount)
.SelectMany(t => t.Item1
? Observable.Return(t.Item2)
: Observable.Throw(t.Item3));
}
public static IObservable DelaySubscription(this IObservable source,
TimeSpan delay, IScheduler scheduler = null)
{
if (scheduler == null)
{
return Observable.Timer(delay).SelectMany(_ => source);
}
return Observable.Timer(delay, scheduler).SelectMany(_ => source);
}
}
Which produces the desired output of
ON ERROR NullReferenceException
Invoked on threadId:11
On nexting 1
On nexting 2
On nexting 3
On complete
Finished on threadId:11
Invoked on threadId:11
On nexting 1
subscriber value is 1 which was seen on threadId:11
On nexting 2
subscriber value is 2 which was seen on threadId:11
On nexting 3
subscriber value is 3 which was seen on threadId:11
On complete
Finished on threadId:11