I was playing with Funscript and wanted to get some result back of my workflow. I added those naive definition (no result cell) to the Async module.
static member RunSynchronously(workflow:Async<'T>, ?timeout:int,?cancellationToken:CancellationToken) =
let result = ref None
let token = defaultArg cancellationToken { Cell = None }
let (Cont f) = workflow
let aux = { StackCounter = ref 0; ExceptionCont = ignore;
CancelledCont = ignore; CancellationToken = token }
f { Cont = (fun v -> result := Some v); Aux = aux }
let r = !result
r.Value
static member StartChild(computation:Async<'T>,?millisecondsTimeout:int) =
async { return Async.FromContinuations(fun (cont, econt,ccnt) -> cont (Async.RunSynchronously computation)) }
Which works in this case
let test = async{ let t = async { let! r = async { return "inside" }
return "Hello" }
let! task = Async.StartChild t
let! res = task
return res
} |> Async.RunSynchronously
But falls over when asked used "for real"
let toto = Globals.document.createElement_img()
toto.id <- "toto"
Globals.document.body.appendChild(toto :> Node) |> ignore
let test = async{ let t = async { let! r = Async.AwaitJQueryEvent(j?toto.load)
return "Hello" }
let! task = Async.StartChild t
do toto.src <- "redundant.png"
let! res = task
return res
} |> Async.RunSynchronously
Because the j?toto.load method does not suspend and call me back, and breaks the async flow. I guess that's what one do with monothreaded javascript.
What would be a real solution to this ? Would implementing supensions, like in F#, be the only way ?
The FunScript implementation of async workflows does not use any advanced threading mechanisms that might be available in JS today. It simply runs everything on the main browser thread - and that's also the reason why it only provides mapping for Async.StartImmediate
because that's what logically corresponds to this behavior in standard F# runtime.
If you want to start a workflow that waits for an event and then run that synchronously, then this is not going to be possible in this model - and so you cannot reasonably implement RunSynchronously
that will always work (to run things synchronously, you need to block the current thread, but then you cannot wait for events...)
So, I think you'll need to restructure your code in some way, so that it does not need synchronous waiting - probably by making the caller asynchronous too.