// NOTE: Works with discreet intervals. Needs refactoring to work with rolling/continuous intervals. public static IObservable <T> LimitTo <T, TPeriod>( this IObservable <T> source, int count, IObservable <TPeriod> periodSource, IScheduler scheduler = null) { return(Observable.Create <T>(o => { scheduler = scheduler ?? Scheduler.Default; var sem = new SemaphoreSlim(0, count); var queue = new ConcurrentQueue <T>(); var queueComplete = new Subject <Unit>(); bool isSourceComplete = false; IConnectableObservable <T> publishedSource = source.Publish(); IDisposable sourceSub = publishedSource .Finally(() => isSourceComplete = true) .Subscribe(Ignore, o.OnError); IObservable <Unit> timer = periodSource .TakeUntil(queueComplete) .Select(_ => { sem.ReleaseMax(count); return (Unit.Default); }); bool GetNext(out T value) { value = default(T); if (queue.IsEmpty) { if (isSourceComplete) { queueComplete.OnNext(Unit.Default); } return false; } return sem.Wait(0) && queue.TryDequeue(out value); } IEnumerable <T> GetQueueValues(Unit dummy) { while (GetNext(out T value)) { yield return value; } } IDisposable sub = publishedSource .Select(x => { queue.Enqueue(x); return Unit.Default; }) .Merge(timer, scheduler) .SelectMany(GetQueueValues) .Subscribe(o); return new CompositeDisposable(sub, sourceSub, publishedSource.Connect(), queueComplete, sem); })); }