在文章Deep Dive into Rx SelectMany中,作者最后在一份说明中提到了以下内容;
注意:为了缓解排序问题,
Foo
附带了一个 带有签名SelectMany()
的选择器的重载。
有人可以告诉我这是如何起作用的吗?
答案 0 :(得分:3)
@ supertopi的回答是正确的。
为了好玩,我想我会制作一个强制输出顺序的扩展方法ToOrdinalOrder
,然后可以将其与SelectMany
拼接在一起,以获得输出顺序匹配的SelectMany
版本输入订单。
public static class Extensions
{
public static IObservable<T> ToOrdinalOrder<T>(this IObservable<T> source, Func<T, int> indexSelector)
{
return source
.Scan((expectedIndex: 0, heldItems: ImmutableDictionary<int, T>.Empty, toReturn: Observable.Empty<T>()), (state, item) =>
{
var itemIndex = indexSelector(item);
if (itemIndex == state.expectedIndex)
{
var toReturn = Observable.Return(item);
var expectedIndex = itemIndex + 1;
var heldItems = state.heldItems;
while (heldItems.ContainsKey(expectedIndex))
{
toReturn = toReturn.Concat(Observable.Return(heldItems[expectedIndex]));
heldItems = heldItems.Remove(expectedIndex);
expectedIndex++;
}
return (expectedIndex, heldItems, toReturn);
}
else
return (state.expectedIndex, state.heldItems.Add(itemIndex, item), Observable.Empty<T>());
})
.SelectMany(t => t.toReturn);
}
public static IObservable<TResult> OrderedSelectMany<TSource, TResult>(this IObservable<TSource> source, Func<TSource, Task<TResult>> selector)
{
return source
.SelectMany(async (TSource item, int index) => (await selector(item), index))
.ToOrdinalOrder(t => t.Item2)
.Select(t => t.Item1);
}
}
以下是一些演示用法的示例代码:
var source = new Subject<string>();
source
.Select((item, index) => (item, index)) //index only required for logging purposes.
.OrderedSelectMany(async t =>
{
var item = t.Item1;
var index = t.Item2;
Console.WriteLine($"Starting item {item}, index {index}.");
switch (index)
{
case 0:
await Task.Delay(TimeSpan.FromMilliseconds(50));
Console.WriteLine($"Completing item {item}, index {index}.");
return (item, index);
case 1:
await Task.Delay(TimeSpan.FromMilliseconds(200));
Console.WriteLine($"Completing item {item}, index {index}.");
return (item, index);
case 2:
await Task.Delay(TimeSpan.FromMilliseconds(20));
Console.WriteLine($"Completing item {item}, index {index}.");
return (item, index);
default:
throw new NotImplementedException();
}
})
.Subscribe(s => Console.WriteLine($"Received item '{s}'."));
source.OnNext("A");
source.OnNext("B");
source.OnNext("C");
source.OnCompleted();
输出如下:
Starting item A, index 0.
Starting item B, index 1.
Starting item C, index 2.
Completing item C, index 2.
Completing item A, index 0.
Received item '(A, 0)'.
Completing item B, index 1.
Received item '(B, 1)'.
Received item '(C, 2)'.
答案 1 :(得分:2)
在所述超载的元数据描述中,它说
// selector:
// A transform function to apply to each element; the second parameter of the function
// represents the index of the source element.
在选择器功能中,您可以访问来自源的通知的值和原始索引。
e.g。如果你需要对一堆值进行一些工作,并知道特定源值的工作何时完成。
public static IObservable<int> WorkAndReportIndex<TSource>(this IObservable<TSource> source)
{
Func<TSource, int, Task<int>> selector = async (value, index) =>
{
await SomeWork(value);
return index;
};
return source.SelectMany(selector);
}