Question

Consider the following definition

let test =
    Async.FromContinuations(
        fun (cont,econt,ccont) ->
            let rec inner () =
                async {
                    do printfn "looping..."
                    do! Async.Sleep 1000
                    return! inner ()
                }

            Async.Start(inner ())
            cont ())

Suppose I want to try the computation like so

let cts = new CancellationTokenSource ()
Async.Start(test, cts.Token)
cts.Cancel()

This will naturally not make the inner loop stop, since I have not passed the suitable cancellation token. Is there any way I can obtain the outer cancellation token through Async.FromContinuations? I could rewrite this using the async builder and Async.CancellationToken, but then I would lose the ability to pass continuations to the inner expression.

Was it helpful?

Solution

smth like this?

let test =
    async {
        let! ct = Async.CancellationToken
        return! Async.FromContinuations(
            fun (cont,econt,ccont) ->
                let rec inner () =
                    async {
                        do printfn "looping..."
                        do! Async.Sleep 1000
                        return! inner ()
                    }

                Async.Start(inner (), cancellationToken = ct)
                cont ())
    }
let cts = new CancellationTokenSource ()
Async.Start(test, cts.Token)
cts.CancelAfter(1000)

OTHER TIPS

Can you describe what are you trying to do? If I understand your code correctly, you want to start the inner loop function in the background and then, in parallel, continue running the rest of the workflow (using the cont() call).

To do this, you do not need Async.FromContinuations. There is a function that does exactly this and it also takes care of handling exceptions, cancellation tokens etc.

I think you could rewrite your program like this:

let test = 
    // The inner loop function from your example
    let rec inner () = async { 
        do printfn "looping..." 
        do! Async.Sleep 1000 
        return! inner ()  } 

    async { 
      // Start the inner loop as a child async 
      let! _ = Async.StartChild(inner())
      // ... continue doing other things in parllel if you wish
      do printfn "main body running..." }

Starting and cancelling of the computation looks as before:

let cts = new CancellationTokenSource () 
Async.Start(test, cts.Token) 
// This will cancel the 'inner' loop as well
cts.Cancel() 

If you call Async.StartChild using let! then it will start the inner task, pass it the cancellation token etc. It returns a token that you can use later to wait until the child task completes, but since you're not doing that, I used the _ pattern.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top