Hi there.
Busy working with some sample chat server code that I read in Dom's new ebook. I found something a bit weird when I added some extra tidy-up logic.
Here is a condensed version that reproduces the issue:
import asyncdispatch, asyncnet
type
Server = ref object
socket: AsyncSocket
proc newServer(): Server = Server(socket: newAsyncSocket())
proc loop(server: Server, port=7687) {.async.} =
echo "binding..."
server.socket.bindAddr(port.Port)
echo "bound..."
echo "listen..."
server.socket.listen()
echo "listening..."
defer:
echo "Closing socket..."
server.socket.close()
echo "Closed."
while true:
echo "Listening for connection..."
let (netAddr, clientSocket) = await server.socket.acceptAddr()
echo("Accepted connection from ", netAddr)
var server = newServer()
waitFor loop(server)
When I compile and run that, I get this output:
binding...
bound...
listen...
listening...
Listening for connection...
Closing socket...
Closed.
Basically it looks a bit like the "defer" logic is triggering before the current scope (loop function) has exited.
I'm guessing that's an implementation detail of how the "async" and "await" statements work behind the scenes - basically defer is getting confused.
Maybe this needs to be added as a new bug/limitation within the documentation over here?
Using async is not very much different with respect to programming with futures or promises in other languages. They are all syntactic sugar over a programming model where you create some code to be executed later and you call it in an event loop.
This means that whenever you perform an asynchronous action, you are only giving an instruction to perform something later. The control flow then goes on until you end the current scope. At that point, the defer part is executed.
It could be added to the documentation, but I do not find it surprising, and certainly not a bug. In fact it is what I would expect to happen. If something different happened, then I would be very confused, because I would have no idea what is happening behind the scenes
Hi there.
Thing is, the "loop" function never actually exits the current scope (I think?).
There is a "while true:" there that ensures this, unless there happens to be some kind of exception raised within the "while" loop.
Basically - my understanding is that async functions are a lot like how python generator functions work.
At the point of an await (or python generator "yield") call, current place in logic is saved, and then the scueduler logic resumes logic at a saved point in some other coroutine (which also never actually "left" the function scope, just basically task-switched out temporarily, also in a cooperative/co-routine type of way).