使用RX observable创建“去抖动”效果

时间:2018-03-03 05:46:48

标签: c# system.reactive

我有一个FTP客户端,我想要连接到FTP服务器,除非(比方说)一分钟没有活动。我想使用Observable来做这件事。

这是一个非常愚蠢的Linqpad脚本,它演示了这个概念:

async Task Main()
{
    var client = new Client();
    client.Connect();

    var debounce = new Subject<int>();
    debounce
        .Throttle(TimeSpan.FromSeconds(1))
        .Subscribe(eventNumber => client.Disconnect(eventNumber));

    // Something uses the FTP client
    debounce.OnNext(1);
    await Task.Delay(200);

    // Something else uses the FTP client
    debounce.OnNext(2);
    await Task.Delay(300);

    // No activity, the client will disconnect
    await Task.Delay(1000);
}

public class Client
{
    public void Connect() => Console.WriteLine("Connected");
    public void Disconnect(int eventNumber) => Console.WriteLine($"Disconnected: {eventNumber}");
}

这很有效 - 客户端在事件“2”之后断开连接。

问题:有更好的方法吗?或者更确切地说,如果不使用Subject

,还有更好的方法吗?

修改

这是一个更加充实的类版本 - 实际上,它订阅了一个observable,它会告诉它一些需要下载的文件;如果没有文件通过一些超时,那么我希望客户端断开连接。

public class MyClassThatDownloadsViaFtp
{
    private IObserver<Unit> _debouncer;
    private FtpClient _client;

    public MyClassThatDownloadsViaFtp(IObservable<FileToDownload> filesToDownloadViaFtp)
    {
        filesToDownloadViaFtp.Subscribe(DownloadFileViaFtp);

        // Disconnect after a minute of activity
        _debouncer = new Subject<Unit>();
        _debouncer
            .Throttle(TimeSpan.FromMinutes(1))
            .Subscribe(_ => DisconnectFtpClient());
    }

    public void DownloadFileViaFtp(FileToDownload file)
    {
        if (_client == null) _client = ConnectFtpClient();

        // Signal that the client is doing some work to prevent disconnect
        _debouncer.OnNext(Unit.Default);
        _client.Download(file.PathOnFtpServer);
    }

    // implementation irrelivent
    private FtpClient ConnectFtpClient() => new FtpClient();
    private FtpClient DisconnectFtpClient() => _client = null;
}

我发现由于我有一个源流,因此可能更容易限制它以达到相同的效果(如下所示);但是,我仍然想知道在拥有可以限制的源流的情况下执行此操作的最佳方法。

public class MyClassThatDownloadsViaFtp 
{
    private FtpClient _client;

    public MyClassThatDownloadsViaFtp(IObservable<FileToDownload> filesToDownloadViaFtp)
    {
        filesToDownloadViaFtp
            .Select(DownloadFileViaFtp)
            .Throttle(TimeSpan.FromMinutes(1))
            .Subscribe(_ => DisconnectFtpClient());
    }

    public Unit DownloadFileViaFtp(FileToDownload file)
    {
        if (_client == null) _client = ConnectFtpClient();
        _client.Download(file.PathOnFtpServer);

        return Unit.Default;
    }

    // implementation irrelivent
    private FtpClient ConnectFtpClient() => new FtpClient();
    private FtpClient DisconnectFtpClient() => _client = null; 
}

1 个答案:

答案 0 :(得分:2)

你基本上用这个回答了你的问题:

public MyClassThatDownloadsViaFtp(IObservable<FileToDownload> filesToDownloadViaFtp)
{
    filesToDownloadViaFtp
        .Select(DownloadFileViaFtp)
        .Throttle(TimeSpan.FromMinutes(1))
        .Subscribe(_ => DisconnectFtpClient());
}

如果您没有像filesToDownloadViaFtp那样方便的流,那么请从Observable.CreateObservable.FromEventObservable.FromEventPattern等创建一个。

一个狡辩:Select理想情况下运行时没有副作用,DownloadFileViaFtp非常有副作用。 Subscribe电话中的副作用最佳。