如何简化F#中的异步编程:基于Playwright库的实现优化咨询
F# 结合 Playwright 的异步代码优化方案
你现有代码的冗余主要来自于每一步异步操作后都单独调用Async.RunSynchronously阻塞线程,完全没有利用F#异步计算表达式的语法糖,和C#里每个await后都接.Wait()/.Result是一样的错误用法,既浪费性能又冗余。
以下是优化后的实现方案:
方案1:保留自定义别名,用async计算表达式重构
async计算表达式和C#的async/await语法逻辑完全对应,只需要统一在最外层执行一次等待即可,不需要每层异步都阻塞:
// 原有别名保留,用来快速转换Task到Async类型 let (~~) = Async.AwaitTask // 建议把Playwright和浏览器初始化逻辑提取到外层,不需要每次获取页码都重复创建实例 let initPlaywright headless = async { let! playwright = ~~Playwright.CreateAsync() let! browser = ~~playwright.Chromium.LaunchAsync(BrowserTypeLaunchOptions(Headless = headless)) return playwright, browser } // 异步方法直接返回Async<int>,和C#返回Task<int>的异步方法逻辑完全一致 let getLastPageNumber (page: IPage) = async { do! ~~page.GotoAsync("https://go.xero.com/BankRec/BankRec.aspx?accountID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&page1") let! lastPageLink = ~~page.QuerySelectorAsync("#mainPagerEnd") if lastPageLink = null then return 1 else let! lastPageHref = ~~lastPageLink.GetAttributeAsync("href") return int lastPageHref }
调用时仅需要在最外层执行一次同步等待即可:
// 同步调用示例 let pageNumber = async { let! _, browser = initPlaywright false use! page = ~~browser.NewPageAsync() return! getLastPageNumber page } |> Async.RunSynchronously
方案2:用F# 6+原生task计算表达式,完全消除冗余
如果你使用的是F# 6及以上版本,可以直接用原生task计算表达式,和C#的Task异步体系完全兼容,连Async.AwaitTask转换都可以省略,代码和C#的async/await写法几乎没有差异:
let getLastPageNumber (page: IPage) = task { do! page.GotoAsync("https://go.xero.com/BankRec/BankRec.aspx?accountID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&page1") let! lastPageLink = page.QuerySelectorAsync("#mainPagerEnd") if lastPageLink = null then return 1 else let! lastPageHref = lastPageLink.GetAttributeAsync("href") return int lastPageHref }
内容的提问来源于stack exchange,提问作者Martin Thompson
相关产品推荐
相关产品推荐

