Ejemplo n.º 1
0
        // 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);
            }));
        }