Http download to disk with fsharp.data.dll files and asynchronous desktops

The following .fsx file is expected to download and save the disk binary table database files that are hosted as links on the internet html page using Fsharp.Data.dll

.

What happens is that the whole thing is delayed after a while and before it gets done without even throwing an exception or the like.

I'm pretty sure I am handling the CopyToAsync()

thing wrong in my asynchronous workflow. Since this should work while I go to take a nap, it would be nice if someone could tell me how this should be done correctly. (More generally, how do you handle the System.Threading.Task thingy in an asynchronous workflow?)

#r @"E:\R\playground\DataTypeProviderStuff\packages\FSharp.Data.2.2.3\lib\net40\FSharp.Data.dll"

open FSharp.Data
open Microsoft.FSharp.Control.CommonExtensions
let document = HtmlDocument.Load("http://www.olympuschess.com/egtb/gaviota/")
let links = 
    document.Descendants ["a"] |> Seq.choose (fun x -> x.TryGetAttribute("href") |> Option.map (fun a -> a.Value()))
    |> Seq.filter (fun v -> v.EndsWith(".cp4"))
    |> List.ofSeq

let targetFolder = @"E:\temp\tablebases\"
let downloadUrls = 
    links |> List.map (fun name -> "http://www.olympuschess.com/egtb/gaviota/" + name, targetFolder + name )

let awaitTask = Async.AwaitIAsyncResult >> Async.Ignore

let fetchAndSave (s,t) =
    async {
        printfn "Starting with %s..." s
        let! result = Http.AsyncRequestStream(s)
        use fileStream = new System.IO.FileStream(t,System.IO.FileMode.Create)
        do! awaitTask (result.ResponseStream.CopyToAsync(fileStream))
        printfn "Done with %s." s
    }

let makeBatches n jobs =
    let rec collect i jl acc =
        match i,jl with
        | 0, _ -> acc,jl
        | _, [] -> acc,jl
        | _, x::xs -> collect (i-1) (xs) (acc @ [x])
    let rec loop remaining acc =
        match remaining with
        | [] -> acc
        | x::xs ->
            let r,rest = collect n remaining []
            loop rest (acc @ [r])
    loop jobs []


let download () = 
    downloadUrls 
    |> List.map fetchAndSave
    |> makeBatches 2
    |> List.iter (fun l -> l |> Async.Parallel |> Async.RunSynchronously |> ignore )
    |> ignore

download()

      

Note Updated code so it creates batches of 2 downloads at a time and only the first batch works. Also added awaitTask from the first answer as that seems to be the correct way to do it.

News What's funny too: if I interrupt a stuck script and then load it again into the same instance of fsi.exe, it disappears immediately. I am starting to think that this is a bug in the library I am using or something like that.

Thanks in advance!

+3


source to share


1 answer


Here fetchAndSave has been modified to handle the task returned from CopyToAsync asynchronously. In your version, you expect synchronously. Your script will be blocked as you are using Async.RunSynchronously to run the entire workflow. However, the files are downloaded as expected in the background.

let awaitTask = Async.AwaitIAsyncResult >> Async.Ignore

let fetchAndSave (s,t) = async {
    let! result = Http.AsyncRequestStream(s)
    use fileStream = new System.IO.FileStream(t,System.IO.FileMode.Create)
    do! awaitTask (result.ResponseStream.CopyToAsync(fileStream))
}

      

Of course, you also need to call



do download()

      

on the last line of your script to start the game.

+2


source







All Articles