0

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 ?

nicolas
  • 9,549
  • 3
  • 39
  • 83

1 Answers1

3

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.

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • Yes the obvious way for this code is to stay within the async. My concern was what exactly breaks down logically, and you pointed at it I think. If I rephrase it, we do not have primitive that block for an event. Because in JS queue model that event will take place after this synchronous code is over. – nicolas Oct 28 '13 at 06:04