Forma correcta de cómo preparar los datos en async cancelables flujo de trabajo con la interfaz de usuario sensible

StackOverflow https://stackoverflow.com//questions/9615810

Pregunta

Esta pregunta se basa en Async.TryCancelled no funciona con Async.RunSynchronously que parece complejo, así que voy a cortar una simple parte en la que trato de resolver.

Supongamos que tengo estas funciones:

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 prepara los datos que se deben mostrar al usuario. updateUI actualiza la interfaz de usuario (elimina los viejos controles y crea nuevas ctl basado en nuevos datos).

Pregunta: ¿Cómo debo llamar a las dos funciones, a fin de que prepareModel es cancelable en cualquier momento?

El flujo es

  • usuario hace clic en actualizar
    • prepareModel(1) inicia y se ejecuta de forma asíncrona, por lo que la interfaz de usuario es sensible y el usuario puede trabajar con la aplicación
  • el usuario cambia los datos y hace clic en actualizar de nuevo
    • prepareModel(1) se cancela y nuevo prepareModel(2) se inicia
  • el usuario cambia los datos y hace clic en actualizar de nuevo
    • prepareModel(2) se cancela y nuevo prepareModel(3) se inicia
    • ..
    • prepareModel(n) terminado
    • updateUI se corrió en el subproceso de interfaz de usuario, actualiza la interfaz de usuario

(Mi primera solución se basa en MailboxProcessor que garantiza que sólo un prepareModel es ejecutado, ver en Async.TryCancelled no funciona con Async.RunSynchronously pero como ya he experimentado con esto, no está libre de errores)

¿Fue útil?

Solución

Un posible enfoque sería para iniciar el flujo de trabajo de forma asincrónica utilizando Async.Start (entonces debe ser cancelable).Para volver a dibujar la interfaz de usuario al final, puede utilizar Async.SwitchToContext para asegurarse de que la última parte del flujo de trabajo se ejecuta en la interfaz de usuario.Aquí es un boceto:

// 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)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top