如何在IObservable订阅启动前添加延迟,延迟接收首个元素?
问题分析与解决方案
你的代码里用Take(TimeSpan)是错误的,这个操作符的作用是在订阅开始后的指定时间窗口内,收集源Observable发射的所有元素,时间窗口结束后就触发序列完成。因为e.ToObservable()会同步一次性发射数组里的所有元素,1秒的时间窗口足够容纳所有元素,所以它们会立刻被打印出来,完全达不到延迟的效果。
要实现“订阅后延迟1秒再接收首个元素”,可以用以下两种常见方案:
方案一:延迟整个序列的订阅(推荐)
使用DelaySubscription操作符,它会让订阅动作延迟指定时间执行,这样整个序列的元素都会在订阅延迟后才开始发射,自然第一个元素就会在1秒后出现:
public static void Main(string[] args) { IEnumerable<string> e = new[] { "Hi", "There", "Bye" }; IObservable<string> strings = e.ToObservable(); // 延迟订阅动作1秒 IObservable<string> stringsTimed = strings.DelaySubscription(TimeSpan.FromSeconds(1)); stringsTimed.Trace("string"); Console.ReadLine(); } public static IDisposable Trace<T>(this IObservable<T> source, string name) { return source.Subscribe ( onNext: t => Console.WriteLine($"{name} -> {t}"), onError: ex => Console.WriteLine($"{name} ERROR: {ex.Message}"), onCompleted: () => Console.WriteLine($"{name} END") ); }
方案二:仅延迟第一个元素
如果需要第一个元素延迟1秒,后面的元素按原节奏发射(对于同步序列来说,后面的元素会紧跟第一个元素),可以用Select结合索引判断,对第一个元素单独添加延迟:
public static void Main(string[] args) { IEnumerable<string> e = new[] { "Hi", "There", "Bye" }; IObservable<string> strings = e.ToObservable(); IObservable<string> stringsTimed = strings .Select((item, index) => index == 0 ? Observable.Return(item).Delay(TimeSpan.FromSeconds(1)) : Observable.Return(item)) .Merge(); // 将多个Observable合并成一个序列 stringsTimed.Trace("string"); Console.ReadLine(); } // Trace方法同上
运行上述代码后,你会看到订阅后等待1秒,才会打印出第一个元素,后续元素按预期发射。
内容的提问来源于stack exchange,提问作者user16095727
相关产品推荐
相关产品推荐

