Отбросьте все события после первого, пока не будет обработано это первое событие - PullRequest
0 голосов
/ 25 марта 2020

У меня есть кнопка, которая открывает чат-бота в браузере. Проблема в том, что у меня нет прямого адреса для чата. Я должен запросить у второго сервера этот адрес, чтобы моя кнопка не реагировала сразу на нажатие.

Я бы хотел, чтобы он не открывал две вкладки браузера, если кнопка нажимается дважды довольно быстро. Мне нужно что-то, похожее на Throttle, но вместо проверки истекшего времени, проверяется, завершилось ли предыдущее событие, которое позволяет go, или нет.

Полагаю, я мог бы сделать это с логическим значением проверка и фильтр в реактивном конвейере, но я не хочу иметь дело с безопасностью потока на этом логическом значении, так как я запускаю процесс во втором потоке.

Например, у меня есть метод, который делает что-то длинное, например, вызывает 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

Но это не очень чисто, не ясно и не модульно, и его легко сломать. Кажется, что это должно быть так просто ... Я, должно быть, что-то здесь упускаю.

Любая помощь будет высоко ценится! Спасибо

1 Ответ

0 голосов
/ 25 марта 2020

Есть несколько способов сделать это:

  • Синхронная обработка задачи - действительно должна выполняться только для очень быстрых вызовов. ~ 50 мс
  • Показать модальное диалоговое окно прогресса - это предотвратит любое взаимодействие с пользовательским интерфейсом до завершения операции, но не остановит пользовательский интерфейс. Лучше всего подходит для длительных операций, которые на самом деле могут показывать прогресс, но могут быть адаптированы для задач различной длины.
  • Отключите кнопку и включите ее после завершения операции - имейте в виду, чтобы использовать try / finally, чтобы убедиться в этом на самом деле повторно включен. И что приложение может обрабатывать любую другую кнопку, нажимаемую во время обработки задачи.
  • Используйте флаг InProgress, как в вашем примере. Если вы просто ожидаете задачу в обработчике кнопок, все, что находится после ожидающего, все равно будет выполняться в главном потоке, поэтому оно совершенно безопасно для потоков.
  • Убедитесь, что нажатие кнопки идемпотентно. Обычно кнопка устанавливает некоторое состояние, но запускает дорогостоящую операцию, только если состояние было изменено.
...