我正在尝试抓取一些需要运行JavaScript的网站,以便该文档拥有我感兴趣的所有数据。我正在尝试打开WebBrowser
并等待文档加载,但是当我尝试切换回WebBrowser
处于打开状态的线程时,无法获得数据。尝试在不切换回线程的情况下运行它会产生转换错误。 =(
是什么阻止async
切换线程?我该如何解决这个问题?
open System
open System.Windows.Forms
open System.Threading
let step a = do printfn "%A" a
let downloadWebSite (address : Uri) (cont : HtmlDocument -> 'a) =
let browser = new WebBrowser()
let ctx = SynchronizationContext.Current
browser.DocumentCompleted.Add (fun _ ->
printfn "Document Loaded" )
async {
do step 1
do browser.Navigate(address)
do step 2
let! _ = Async.AwaitEvent browser.DocumentCompleted
do step 3
do! Async.SwitchToContext ctx
do step 4
return cont browser.Document }
let test =
downloadWebSite (Uri "http://www.google.com") Some
|> Async.RunSynchronously
>
1
2
Document Loaded
3
# It just hangs here. I have to manually interrupt fsi.
- Interrupt
>
4
答案 0 :(得分:0)
您的方法存在的问题是RunSynchronously
阻塞了您试图使用Async.SwitchToContext ctx
运行其余异步计算的线程。
使用F#Interactive时,有一个主线程在F#Interactive中运行并处理用户交互。这是可以使用Windows窗体控件的线程,因此您可以在WebBrowser
之外正确创建async
。等待DocumentCompleted
发生在线程池线程(运行异步工作流)上,但是当您尝试切换回主线程时,它已被Async.RunSynchronously
阻塞。
您可以通过运行一个调用Application.DoEvents
的循环来处理主线程上的事件来避免阻塞线程(这也将使其运行异步的其余部分)。您的downloadWebSite
保持不变,但现在您等待使用:
let test =
downloadWebSite (Uri "http://www.google.com") Some
|> Async.Ignore
|> Async.StartAsTask
while not test.IsCompleted do
System.Threading.Thread.Sleep(100)
System.Windows.Forms.Application.DoEvents()
这有点hack-如果您真的不需要等待结果(例如,只需返回一个任务并在运行下一个命令之前等待),则可能会有更好的结构化方式。做到这一点。