Question

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

  • user clicks refresh
    • prepareModel(1) started and is running asynchronously, so the UI is responsive and user can work with the application
  • user changes data and clicks refresh again
    • prepareModel(1) from is cancelled and new prepareModel(2) is started
  • user changes data and clicks refresh again
    • prepareModel(2) is cancelled and new prepareModel(3) is started
    • ..
    • prepareModel(n) finished
    • updateUI 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)

Was it helpful?

Solution

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)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top