6

I need to call in the background a API that call a webservice. I don't wish to turn the (very complex) method to async, just say "do all this on the background".

But I'm lost in how do this with F#. This is what I have:

            let task = async {
                let result = SyncApi.syncData(login.url, login.zone, login.user, login.pwd) <-- THIS MUST RUN IN BACKGROUND...
                match result with
                |Some(msg) -> failwith msg
                | None -> ()
            }

            task
            |> Async.Catch
            |> Async.RunSynchronously
            |> fun x -> 
                  match x with
                  | Choice1Of2 x -> rootPage.Navigation.PopToRootAsync(true) |> ignore
                  | Choice2Of2 ex -> showMsgError(ex.Message)
mamcx
  • 15,916
  • 26
  • 101
  • 189

1 Answers1

6

If you're looking for simple fire and forget style to start the API call an don't use the result on the current thread, Async.Start(task) might be what you're looking for. It takes the Async workflow, starts it on a thread pool and immediately returns so that your method can continue.

But seeing that you need the result to either change navigation or show an error message, you might need to call the SyncApi synchronously on the current thread and wait for its result.

Alternatively, if your application model allows it, you can do something like this:

(* Define the task including error handling. *)
let task = async {
    let result = SyncApi.syncData(login.url, login.zone, login.user, login.pwd)
    match result with
    | Some msg ->
        (* This may have to be posted back to the UI context.
           Correct way depends on technology (Xamarin vs. WPF vs. MVC...) *)
        showMsgError msg
    | None -> ()
}

(* Fire and forget the async API call. *)
Async.Start(task)

(* Optimistically navigate away immediately,
   while `task` may still be in progress. *)
rootPage.Navigation.PopToRootAsync(true) |> ignore

This will start the task on a thread pool, navigate away, but in case the async task failed, it will trigger the error message. However it assumes that your application can show the error message asynchronously for example as a popup, not only in the context of the page that started the task.

Honza Brestan
  • 10,637
  • 2
  • 32
  • 43
  • Why do you *need* to call the API on the background in the first place? If it's to make the UI responsive during processing but can't show error message asynchronously, I'm afraid I know no other way than to make the whole method asynchronous. If you want to make the method faster, the synchronization point, showing the error, is the problem - it won't go away, it has to wait for the processing result. – Honza Brestan Nov 11 '17 at 21:15
  • Yes is for make the UI responsive. In swift is easy to do this, I just schedule the task in another thread: https://stackoverflow.com/questions/24056205/how-to-use-background-thread-in-swift – mamcx Nov 12 '17 at 02:54
  • `dispatch_async` is essentially `Async.Start`, isn't it? You still have to dispatch the continuation (showing the error message) back to the foreground thread (the main queue) - what does the foreground method do in the meantime? How does it wait for the result if it cannot navigate elsewhere to wait for (possible) asynchronous error message? – Honza Brestan Nov 12 '17 at 07:33
  • Look at the "completion" callback – mamcx Nov 12 '17 at 23:19
  • If I understand it correctly (it's been some time since I did anything for iOS), the `completion` callback is just what is passed to be dispatched back to the UI thread, so in my example it's the `showMsgError msg` inside the async workflow. From just this I don't know what the main queue is doing in the meantime - how does a Swift function know to stop doing anything, process another message and then get back. Because it looks like the Swift example is more like async version of the F# function, not a synchronous one.. – Honza Brestan Nov 13 '17 at 09:07