반응형 UI를 사용하여 비동기 취소 가능 워크플로에서 데이터를 준비하는 올바른 방법
-
09-12-2019 - |
문제
이 질문은 다음을 기반으로 합니다. Async.TryCancelled가 Async.RunSynchronously와 작동하지 않습니다. 복잡해 보여서 제가 해결하려고 하는 간단한 부분만 잘라내겠습니다.
다음과 같은 기능이 있다고 가정해 보겠습니다.
let prepareModel () =
async {
// this might take a lot of time (1-50seconds)
let! a = ...
let! b = ...
let! res = combine a b
return res
}
let updateUI model =
runOnUIThread model
prepareModel
사용자에게 표시되어야 하는 데이터를 준비합니다. updateUI
UI를 새로 고칩니다(이전 컨트롤을 제거하고 새 데이터를 기반으로 새 ctls를 만듭니다).
질문: 두 함수를 어떻게 호출해야 합니까? prepareModel
언제든지 취소할 수 있나요?
흐름은
- 사용자가 새로고침을 클릭함
prepareModel
(1) 비동기적으로 시작되어 실행 중이므로 UI가 반응하고 사용자가 애플리케이션으로 작업할 수 있습니다.
- 사용자가 데이터를 변경하고 다시 새로고침을 클릭합니다.
prepareModel
(1) from이 취소되고 새로운prepareModel
(2)가 시작되었습니다
- 사용자가 데이터를 변경하고 다시 새로고침을 클릭합니다.
prepareModel
(2) 취소되고 새로 제공됩니다prepareModel
(3)이 시작되었습니다- ..
prepareModel
(n) 완료updateUI
UI 스레드에서 실행되고 UI를 다시 그립니다.
(내 첫 번째 솔루션은 다음을 기반으로합니다. MailboxProcessor
이는 단 하나만 보장합니다. prepareModel
실행됩니다. 다음을 참조하세요. Async.TryCancelled가 Async.RunSynchronously와 작동하지 않습니다. 하지만 이것을 실험해 본 결과 버그가 없는 것은 아닙니다)
해결책
한 가지 가능한 접근 방식은 다음을 사용하여 백그라운드에서 비동기적으로 워크플로를 시작하는 것입니다. Async.Start
(그러면 취소 가능해야 합니다).마지막에 UI를 다시 그리려면 다음을 사용할 수 있습니다. Async.SwitchToContext
워크플로의 마지막 부분이 UI에서 실행되는지 확인합니다.다음은 스케치입니다.
// Capture current synchronization context of the UI
// (this should run on the UI thread, i.e. when starting)
let syncContext = System.Threading.SynchronizationContext.Current
// Cancellation token source that is used for cancelling the
// currently running workflow (this can be mutable)
let cts = ref (new CancellationTokenSource())
// Workflow that does some calculations and then updates gui
let updateModel () =
async {
// this might take a lot of time (1-50seconds)
let! a = ...
let! b = ...
let! res = combine a b
// switch to the GUI thread and update UI
do! Async.SwitchToContext(syncContext)
updateUserInterface res
}
// This would be called in the click handler - cancel the previous
// computation, creat new cancellation token & start the new one
cts.Cancel()
cts := new CancellationTokenSource()
Async.Start(updateModel(), cts.Token)
제휴하지 않습니다 StackOverflow