在某些情况下,ToObservable无法触发

时间:2019-08-23 17:52:10

标签: system.reactive

默认情况下,RX是同步的,因此我们可以确认

            int j = 0;
            Observable.Range(1, 2)
                .SelectMany(i => {
                    return new[]{1}.ToObservable()
                            .Select(i1 => {
                                new[]{1}.ToObservable().Subscribe(i2 => j = 1);
                                return 0;
                            })
                        ;
                })
                .Subscribe();
            j.ShouldBe(1);

但是在我的代码库中,我有一个类似的查询,除非我使用即时调度程序,否则该查询不会触发。

 public static IObservable<GitHubIssue> Save(this IObservable<IReadOnlyList<Issue>> source,  IGitHubRepository repository){
            var objectSpace = repository.ObjectSpace;
                return source.SelectMany(list => list.ToObservable().Select(issue => {
                    var gitHubIssue = objectSpace.CreateObject<GitHubIssue>();
                    gitHubIssue.Id = issue.Id;
                    issue.Labels.ToObservable(Scheduler.Immediate).Select(label => {
                        var gitHubLabel =objectSpace.GetObjectsQuery<GitHubLabel>(true).FirstOrDefault(_ => label.Name == _.Name) ??
                                         objectSpace.NewGitHubLabel(label);
                        gitHubIssue.Labels.Add(gitHubLabel);
                        return gitHubLabel;
                    }).Subscribe();
                    //previous selector is not executed 

我看不到这种关系以及为什么会发生这种情况

1 个答案:

答案 0 :(得分:4)

“默认情况下,RX是同步的”-不,不是。每个运算符都有自己的默认值。

Observable.Range为例。以下是您不提供Scheduler时的实现方式:

public virtual IObservable<int> Range(int start, int count)
{
    return Range_(start, count, SchedulerDefaults.Iteration);
}

依次使用:

internal static IScheduler Iteration
{
    get
    {
        return CurrentThreadScheduler.Instance;
    }
}

如果我以Observable.Timer作为对立点,则我有以下代码:

public virtual IObservable<long> Timer(TimeSpan dueTime)
{
    return Timer_(dueTime, SchedulerDefaults.TimeBasedOperations);
}

使用哪个:

internal static IScheduler TimeBasedOperations
{
    get
    {
        return DefaultScheduler.Instance;
    }
}

您必须放入.ToObservable(Scheduler.Immediate)的事实表明您有一个默认情况下不使用Scheduler.Immediate的运算符。

现在,忽略所有这些,在可观察的管道中永远不应该做的事情就是订阅另一个可观察的管道。永远不能。当您执行此操作时,您将依赖副作用,这就是代码中出现的问题。

您应始终假定对Subscribe的任何调用都在将来的某个时间运行,因此即使您断言j.ShouldBe(1)都不应在Subscribe之后使用。

您的示例代码应更像这样:

int j = 0;
Observable
    .Range(1, 2)
    .SelectMany(i =>
    {
        return
            new[] { 1 }
                .ToObservable()
                .Select(i1 =>
                {
                    return 1;
                })
            ;
    })
    .Subscribe(x => 
    {
        j = x;
        /* j only valid here */
    });

/* j NOT valid here */

副作用的理性具体示例:

int j = 0;
Observable
    .Delay(Observable.Return(42), TimeSpan.FromSeconds(2.0))
    .Do(x => j = x)
    .Subscribe();
Console.WriteLine(j);

最终,j等于42,但在调用Console.WriteLine(j)时不等于。永远不要依赖于可观察范围内已更新的可观察范围之外的状态。