This question is based on Async.TryCancelled doesn't work with Async.RunSynchronously that looks complex, so I will cut a simple part that I try to solve.
Suppose I have this functions:
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
prepares data that should be displayed to the user. updateUI
refreshes the UI (removes old controls and creates new ctls based on new data).
Question: How should I call the two functions so that prepareModel
is cancellable any time?
The flow is
prepareModel
(1) started and is running asynchronously, so the UI is responsive and user can work with the applicationprepareModel
(1) from is cancelled and
new prepareModel
(2) is startedprepareModel
(2) is cancelled and
new prepareModel
(3) is startedprepareModel
(n) finishedupdateUI
is ran on UI thread, redraws the UI(My first solution is based on MailboxProcessor
that ensures that only one prepareModel
is executed, see at Async.TryCancelled doesn't work with Async.RunSynchronously but as I experimented with this, it's not bug free)
One possible approach would be to start the workflow asynchronously in the background using Async.Start
(then it should be cancellable). To redraw the UI at the end, you can use Async.SwitchToContext
to make sure that the last part of the workflow executes on the UI. Here is a sketch:
// 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)