【问题标题】:F#: Downloading data asynchronouslyF#:异步下载数据
【发布时间】:2015-03-31 11:19:52
【问题描述】:

我是编程新手,F# 是我的第一语言。

以下是我的代码的相关部分:

open System.IO
open System.Net

let downloadHtmlFromUrlAsync (url: string) =
    async { 
        let uri = new System.Uri(url)
        let webClient = new WebClient()
        let! html = webClient.AsyncDownloadString(uri)
        return html
        }

let downloadHtmlToDisk (url: string) (directoryPath: string) = 
    if isValidUrl url then
        let name = getNameFromRedirectedUrl url
        let id = getIdFromUrl url
        let html = downloadHtmlFromUrlAsync url
        let newTextFile = File.Create(directoryPath + "\\" + id.ToString("00000") + " " + name.TrimEnd([|' '|]) + ".html")
        use file = new StreamWriter(newTextFile) 
        file.Write(html) 
        file.Close()

let downloadEntireDatabase (baseUrl: string) (totalNumberOfPeople: int) = 
    let allIds = [ for i in 1 .. totalNumberOfPeople -> i ]

    allIds
    |> Seq.map (fun id -> baseUrl + string(id))
    |> Seq.filter isValidUrl
    |> Seq.map downloadHtmlToDisk
    |> Async.Parallel 
    |> Async.RunSynchronously

我已经在 F# 交互中测试了函数 isValidUrl、getNameFromRedirectedUrl、getIdFromUrl。它们工作正常。

我的问题是:当我尝试运行上面粘贴的代码时,会产生以下错误消息:

Program.fs(483,8):错误 FS0193:类型约束不匹配。方式 seq<(string -> unit)> 与类型不兼容 seq<Async<'a>> 类型Async<'a>string -> unit 类型不匹配

出了什么问题?我应该做出哪些改变?

【问题讨论】:

  • 至少在哪里:downloadFighterHtmlToDisk?

标签: asynchronous f#


【解决方案1】:

问题大概是这行(能不能给我们downloadFighterHtmlToDisk的定义):

  allIds
    ...
    |> Seq.map downloadFighterHtmlToDisk
    ...

根据错误消息,此函数似乎有签名string -> string -> unit,但您确实需要string -> Async<'something>

现在我猜你使用了downloadHtmlToDisk 或类似的东西,你可以但我建议将其重写为:

let downloadHtmlToDisk (directoryPath: string) (url: string) = 
    async {
        if isValidUrl url then
            let name = getNameFromRedirectedUrl url
            let id = getIdFromUrl url
            let! html = downloadHtmlFromUrlAsync url
            let newTextFile = File.Create(directoryPath + "\\" + id.ToString("00000") + " " + name.TrimEnd([|' '|]) + ".html")
            use file = new StreamWriter(newTextFile) 
            file.Write(html) 
    }

并像使用它

 let downloadEntireDatabase (baseUrl: string) (totalNumberOfPeople: int) = 
        let allIds = [ for i in 1 .. totalNumberOfPeople -> i ]

        allIds
        |> Seq.map (fun id -> (id, baseUrl + string(id)))
        |> Seq.filter (fun (_,url) -> isValidUrl url)
        |> Seq.map (fun (id,url) -> downloadHtmlToDisk (getFighterPath id) url)
        |> Async.Parallel 
        |> Async.RunSynchronously

看到let! html = ..?这很重要——这就是async 发生的地方;)——如果你愿意,你可以找到类似的操作来异步写入你的文件。你也不需要关闭你的文件 - dispose 应该处理它

备注

我刚刚看到你从 url 中重新提取了 id - 你也可以使用它而不是我使用元组的方式,但我认为如果你仍然需要它,最好真正传递 id - 例如在@987654330 @你真的需要id,并且可以从id那里创建url——这是一种更简单的方法IMO,但我不想重写你所做的一切——只是用这些东西做一些试验

【讨论】:

  • 谢谢,卡斯滕。我有一个后续问题:当我调用 Async.Parallel 时,如何指定要使用的最大线程数?
  • 你不能直接 - 基本上你必须将工作分组到你想要的块中并将它们传递给 Async.Parallel - 或者信任 ThreadPool ;)
猜你喜欢
  • 2011-09-07
  • 1970-01-01
  • 2011-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多