Непрерывный цикл F# в F#

у меня есть сервер сокетов, который должен работать на цикле, принимающем клиентов, поэтому я узнал, что в функциональном программировании используется рекурсивный цикл:

let awaitConnections (wsl:WebSocketListener) = 
    let rec loop ()=
        async { 
            let! ws = Async.AwaitTask (wsl.AcceptWebSocketAsync(cancellation.Token) )
            printfn "Connection from %s" (ws.RemoteEndpoint.Address.ToString())
            Async.Start <| awaitMessages ws
            do! loop()}
    loop()

и этот код вызывается следующим образом:

Async.Start <| awaitConnections listener

учитывая, что приложение работает непрерывно, следует ли использовать итеративный подход вместо этого? Делает rec подход создает вложенные стеки казнь?

кроме того, я хотел бы сделать что-то после цикла заканчивается, например:

let awaitConnections (wsl:WebSocketListener) = 
    let rec loop ()=
        async { 
            let! ws = Async.AwaitTask (wsl.AcceptWebSocketAsync(cancellation.Token) )
            printfn "Connection from %s" (ws.RemoteEndpoint.Address.ToString())
            Async.Start <| awaitMessages ws
            do! loop()}
    loop()
    printf "The loop ended" // <-- this line

но тогда он не может скомпилировать, потому что awaitConnections тип возвращаемого значения. Как я мог это сделать? Я все делаю правильно?

1 ответов


вы, безусловно, на правильном пути! Вот простой пример, который демонстрирует, что вам нужно сделать:

// Loop that keeps running forever until an exception happens
let rec loop () = async {
  do! Async.Sleep(1000)
  printfn "Working"
  return! loop () }

// Call the loop in a try .. finally block to run some cleanup code at the end
let main () = async {
  try 
    do! loop () 
  finally 
    printfn "That's it!" }

// Start the work in the background and create a cancellation token
let cts = new System.Threading.CancellationTokenSource()
Async.Start(main (), cts.Token)
// To cancel it, just call: cts.Cancel()

несколько важных моментов:

  • вы не можете запустить код после завершения бесконечного цикла (он бесконечен!), но вы можете использовать try .. finally для запуска некоторого кода при отмене блока

  • обратите внимание, что с помощью return! для рекурсивного цикла лучше использовать do! создает памяти подтеки.

  • вы можете отменить вычисление с помощью токена отмены-просто передайте токен при его запуске.