如何在 F# 中简化异步编程
How to Simplify Asynchronous Programming in F#
我有 C# 背景,使用过 async/await。我正在尝试使用库找到一种“不那么冗长”的编程方式。
(特别是用于浏览器自动化的 Microsoft Playwright 库)
let (~~) = Async.AwaitTask
let getLastPageNumber(page: IPage) =
let playwright = ~~Playwright.CreateAsync() |> Async.RunSynchronously
let browser = ~~playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions(Headless = false )) |> Async.RunSynchronously
~~page.GotoAsync("https://go.xero.com/BankRec/BankRec.aspx?accountID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&page1") |> Async.RunSynchronously |> ignore
let lastPageLink = ~~page.QuerySelectorAsync("#mainPagerEnd") |> Async.RunSynchronously
if lastPageLink = null then
//this is the last page
1
else
let lastPageNumber = ~~lastPageLink.GetAttributeAsync("href") |> Async.RunSynchronously
lastPageNumber |> int
我使用 Async.AwaitTask 的别名 ~~ 缩短了一些内容,但似乎需要很多代码才能完成一些在 C# 中容易得多的事情。
Async.RunSynchronously
只能作为最后的手段使用,因为它会阻塞一个线程来执行计算,这违背了使用 async/tasks.
的目的
F# 等同于 C# 的 async/await 是使用 F# 的 Async
类型和 async
计算表达式。但是,如果您使用的是使用 .NET Task
类型的 .NET 库,那么您可以使用具有 task
计算表达式的 TaskBuilder.fs 库。
那么你可以这样写函数:
open FSharp.Control.Tasks
let getLastPageNumber(page: IPage) = task {
let! playwright = Playwright.CreateAsync()
let! browser = playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions(Headless = false ))
let! _ = 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
//this is the last page
return 1
else
let! lastPageNumber = lastPageLink.GetAttributeAsync("href")
return lastPageNumber |> int
}
在计算表达式(也称为构建器)中let!
用于等待任务。
请注意,此函数现在 returns Task<int>
而不是 int
,因此调用者可能需要遵循类似的模式并进一步传播任务。
您可以阅读有关在 F# here 中使用 async
的更多信息。其中一些知识也可以应用于 task
计算表达式。
我有 C# 背景,使用过 async/await。我正在尝试使用库找到一种“不那么冗长”的编程方式。 (特别是用于浏览器自动化的 Microsoft Playwright 库)
let (~~) = Async.AwaitTask
let getLastPageNumber(page: IPage) =
let playwright = ~~Playwright.CreateAsync() |> Async.RunSynchronously
let browser = ~~playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions(Headless = false )) |> Async.RunSynchronously
~~page.GotoAsync("https://go.xero.com/BankRec/BankRec.aspx?accountID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&page1") |> Async.RunSynchronously |> ignore
let lastPageLink = ~~page.QuerySelectorAsync("#mainPagerEnd") |> Async.RunSynchronously
if lastPageLink = null then
//this is the last page
1
else
let lastPageNumber = ~~lastPageLink.GetAttributeAsync("href") |> Async.RunSynchronously
lastPageNumber |> int
我使用 Async.AwaitTask 的别名 ~~ 缩短了一些内容,但似乎需要很多代码才能完成一些在 C# 中容易得多的事情。
Async.RunSynchronously
只能作为最后的手段使用,因为它会阻塞一个线程来执行计算,这违背了使用 async/tasks.
F# 等同于 C# 的 async/await 是使用 F# 的 Async
类型和 async
计算表达式。但是,如果您使用的是使用 .NET Task
类型的 .NET 库,那么您可以使用具有 task
计算表达式的 TaskBuilder.fs 库。
那么你可以这样写函数:
open FSharp.Control.Tasks
let getLastPageNumber(page: IPage) = task {
let! playwright = Playwright.CreateAsync()
let! browser = playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions(Headless = false ))
let! _ = 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
//this is the last page
return 1
else
let! lastPageNumber = lastPageLink.GetAttributeAsync("href")
return lastPageNumber |> int
}
在计算表达式(也称为构建器)中let!
用于等待任务。
请注意,此函数现在 returns Task<int>
而不是 int
,因此调用者可能需要遵循类似的模式并进一步传播任务。
您可以阅读有关在 F# here 中使用 async
的更多信息。其中一些知识也可以应用于 task
计算表达式。