dotnet / dotnet/reactive

Behavioral difference between Concat and Merge(1)

Open
#1,634 0 comments 2 reactions 0 assignees View on GitHub
[area] Rx
Dominant language
C#
Stars
7.2k
Forks
798
PR merge metrics
No merged PRs in 30d

Description

Hi! I was under the impression that the `Concat` operator is equivalent to the `Merge(1)` operator, since both operators subscribe at one subsequence at a time ([this](https://github.com/dotnet/reactive/pull/491) pull request reinforces this impression). But I discovered a difference. If the `source` sequence fails, the `Concat` operator waits until the currently subscribed subsequence completes before propagating the error. On the contrary the `Merge(1)` disposes immediately the subscription to the currently subscribed subsequence. Here is a minimal demonstration of this behavior:

```C#
using System;
using System.Reactive.Linq;
using System.Threading.Tasks;

public class Program
{
public static async Task Main()
{
var sequence = Observable
.Create(async (observer, ct) =>
{
observer.OnNext(1);
observer.OnNext(2);
await Task.Delay(500, ct);
observer.OnError(new Exception());
})
.Select(x => Observable.FromAsync(async (ct) =>
{
Console.WriteLine($"Item {x} started");
try { await Task.Delay(1000, ct); }
catch (OperationCanceledException) { Console.WriteLine($"Item {x} aborted"); throw; }
Console.WriteLine($"Item {x} finished");
return x;
}))
.Concat()
//.Merge(1)
.Do(x => Console.WriteLine($"Received: {x}"));

try { await sequence.DefaultIfEmpty(); }
catch (Exception ex) { Console.WriteLine($"Await failed ({ex.Message})"); }

await Task.Delay(200);
}
}
```

Output with `Concat`:

```none
Item 1 started
Item 1 finished
Received: 1
Await failed (Exception of type 'System.Exception' was thrown.)
```

Output with `Merge(1)`:

```none
Item 1 started
Await failed (Exception of type 'System.Exception' was thrown.)
Item 1 aborted
```

[Try it on Fiddle](https://dotnetfiddle.net/LukIwd).

I would like to ask if this difference in behavior between the two operators is by design.

Contributor guide

No contributing guide indexed for this repository

Research direction

Run the minimal C# reproduction in the issue and compare the logged behavior of Concat with Merge(1), focusing on source errors and cancellation of the active subsequence. Check the operator implementations and existing tests for these error and disposal semantics; done means the behavior is either confirmed as intentional or a specific corrective change is agreed.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
developer-experience
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.