F#:异步下载数据
F#: Downloading data asynchronously
我是编程新手,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# interactive 中测试了函数 isValidUrl、getNameFromRedirectedUrl、getIdFromUrl。他们工作得很好。
我的问题是:当我尝试 运行 上面粘贴的代码时,会产生以下错误消息:
Program.fs(483,8): error FS0193: Type constraint mismatch. The type
seq<(string -> unit)>
is not compatible with type
seq<Async<'a>>
The type Async<'a>
does not match the type string -> unit
出了什么问题?我应该做哪些改变?
问题可能出在这一行(能否给我们定义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 - 例如在 downloadHtmlToDisk
中你真的需要 id
并且可以从那里的 id
创建 url
- 一个更简单的方法 IMO 但我不想重写你所做的一切——只是用这些东西做一点实验
我是编程新手,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# interactive 中测试了函数 isValidUrl、getNameFromRedirectedUrl、getIdFromUrl。他们工作得很好。
我的问题是:当我尝试 运行 上面粘贴的代码时,会产生以下错误消息:
Program.fs(483,8): error FS0193: Type constraint mismatch. The type
seq<(string -> unit)>
is not compatible with typeseq<Async<'a>>
The typeAsync<'a>
does not match the typestring -> unit
出了什么问题?我应该做哪些改变?
问题可能出在这一行(能否给我们定义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 - 例如在 downloadHtmlToDisk
中你真的需要 id
并且可以从那里的 id
创建 url
- 一个更简单的方法 IMO 但我不想重写你所做的一切——只是用这些东西做一点实验