【问题标题】:Drop all events after the first until that first event has been processed删除第一个事件之后的所有事件,直到第一个事件被处理
【发布时间】:2020-03-25 14:24:19
【问题描述】:

我有一个在浏览器中打开聊天机器人的按钮。问题是我没有直接知道聊天机器人的地址。我必须向第二台服务器询问该地址,所以我的按钮不会立即对点击做出反应。

如果按钮被快速单击两次,我希望它不要打开两个浏览器选项卡。 我需要一些看起来有点像 Throttle 的东西,但不是检查经过的时间,而是检查它放开的上一个事件是否已经终止。

我想我可以在反应式管道中使用布尔检查和过滤器来做到这一点,但我不想处理该布尔值上的线程安全性,因为我在第二个线程上启动进程。

例如,我有一个方法很长,比如调用 api 来获取地址:

private async Task<string> LongTask()
{
    await Task.Delay(1000);
    return "a return value";
}

我想一次处理一次,放弃在第一个事件完成处理之前发生的任何其他点击,有点像:

var longTaskObservable = 
    Observable.FromAsync(LongTask) // Create observable from async method that returns one value
              .SubscribeOn(NewThreadScheduler.Default); // Run it on a background thread

bool inProgress = false;
Observable.FromEventPattern(btn, "Click") // On a click event
    .Where(_ => !inProgress)
    .Do(_ => inProgress = true)
    .SelectMany(longTaskObservable) // Start the long task observable and use its unwrapped values instead of the event args
    .Where(s => !string.IsNullOrEmpty(s)) // Exclude any results that are marked as invalid
    .ObserveOnDispatcher() // Set the thread of the subscribe method after to the ui thread
    .Subscribe(result => {
        LogWithThread("Got result '" + result + "' on thread {0}");
        inProgress = false;
    }); // Get the result

但这不是很干净或清晰或模块化,很容易破坏。 看起来应该很简单。我一定是在这里遗漏了一些东西。

任何帮助将不胜感激! 谢谢

【问题讨论】:

  • 您不能在单击时禁用按钮并在进程结束时重新启用它吗?
  • 这基本上就是我在帖子中概述的内容。如果存在,我正在寻找一种反应式原生解决方案。或者至少更优雅的东西

标签: c# .net async-await reactive-programming system.reactive


【解决方案1】:

有多种方法可以做到这一点:

  • 同步处理任务 - 只应真正用于非常快速的调用。 ~50ms
  • 显示模式进度对话框 - 这将阻止与 UI 的任何交互,直到操作完成,但不会冻结 UI。最适合可以实际显示进度的长时间操作,但可能适用于不同长度的任务。
  • 禁用按钮并在操作完成后启用它 - 请记住使用 try/finally 以确保它实际上是重新启用的。并且应用程序可以处理任务处理过程中按下的任何其他按钮。
  • 使用 InProgress 标志,如您的示例所示。如果您只是在按钮处理程序中等待任务,则等待之后的任何内容仍将在主线程上运行,因此它是完全线程安全的。
  • 确保按钮按下是幂等的。通常,按钮会设置一些状态,但只有在状态改变时才会运行昂贵的操作。

【讨论】:

  • 它们都是 UI 问题的相当不错的解决方案,但正如你所说,我们仍然必须处理任何故障情况,我认为响应式可能有一些经过深思熟虑的功能来应对所有事情。如果我们开始在 observables 中添加错误处理,我声明它会变得非常混乱并且很容易被后续更改破坏。
  • 我意识到这个问题可以通过在事件中使用 async/await 来解决,但我别有用心地想将这种反应式管道解决方案移植到 F#想知道在响应式范围内是否有更好的方法
  • 对于这个特定的例子,我看不出 Rx 如何让问题变得更简单。我个人可能会坚持使用常规的 asyn/await 和 try/catch 来处理错误。
  • 我不认为这会让事情变得更容易,但正如我所说,这是尝试将这种类型的解决方案转移到更自然的 F# 的一部分
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-02
  • 1970-01-01
  • 1970-01-01
  • 2017-03-04
  • 1970-01-01
  • 1970-01-01
  • 2011-12-30
相关资源
最近更新 更多