So, I am trying to implement async io in zig using the new Io interface. I understand writing my own event loop, but the mental model is still not clear.
Any good example code or links would be really appreciated.
I think you are on the wrong track. I think it would really help if you could describe what you are trying to program. Talking about async/await is not helpful, because in JS, these are keywords to declare and call stackless coroutines, a concept that doesn’t exist in Zig.
In Zig 0.16, everything you see looks like regular blocking code, even if it uses non-blocking primitives under the hood. The Io interface is there specifically to make it not relevant what the implementation does.
If you want to wait, you use io.sleep. If you want to read from a socket, you call stream.reader(io, &buffer) on a TCP stream and use the reader interface as usual.
You only need to use io.async or io.concurrent if you want to do things “in the background”, for example:
Thanks for the reply. And seeing your code in zio I know you have done a lot of work and brainstorming on this. Let me come to you with a good implementation example. Or rather a really good question. I think you are the best person around here to answer my doubts
I am brainstorming on how can I promisify this function. Like in the event loop it should only be resolved when its afternoon
Please consider that everything in this thread is a hypothetical brainstorming. I will be writing a proper post/thread later on with working examples. Right now I am only brainstorming
Right now, there isn’t anything special you need to do for a function to work with std.Io. This can change in the future when stackless coroutines are introduced.
For something like this, you use io.concurrent() to spawn two tasks for the functions, in them you do io.sleep() and then return a random value. In the parent function you await() the tasks and combine the results.
Note that this is going to be extremely wasteful with std.Io.Threaded, but that’s the only way to do it. The Io interface doesn’t provide you any kind of event loop to hook into. With the future std.Io.Evented, it’s a lot less wasteful, depending on how they end up implementing stacks. With zio, it’s mostly fine, the cost is minimal, plus you have to option to access the event loop, where the cost of waiting like this is about zero.
The Io interface is not very helpful with these kind of futures/promises. You would basically need to implement this yourself in terms of having your own mini event loop in one task, and use e.g. std.Io.Event so that you have something to wait on in the tasks that need the value.
If you absolutely need something like this, using zio directly might give you more options, but you would have to touch the lower level APIs, but then you lose some compatibility with the rest of other Zig ecosystem.
However, I’d really start with the goal of what you want to do. If you want to think of Io in terms of “async” code, you will not be very happy. The goal of the interface is to escape that model, give you blocking APIs to work with and structure the code the way you would if you used threads.
Here’s one of the Zig core team members explaining some of the theory behind the new IO interface:
Like has been mentioned, the goal isn’t JS-style async programming, but to provide an IO model that’s agnostic of the concurrency implementation, so all client code will look the same but can run in different contexts as needed.
Yes. I figured it out. Let me come up with a mini-DSL. I think I am getting onto something here. I want you to review it. I went over your zio runtime. Its awesome. @lalinsky
While you may disagree with me, the mental model behind JS’s async/await is one of the best I have seen for single-threaded runtimes. Just the mental model. I really don’t like Go’s concurrency (although it helps with multi-threading) in terms of the model
I know the only_morning_fn is not computationally bound or anything. Its just a basic example to understand the mental model. That is definitely not something I would put up in an event loop. lol
I started working on zio after I tried to use libuv (the lib that node.js uses) in Zig and really really disliked the callback APIs. The fact that you need to allocate closures makes it extremely awkward to use. Therefore I started exploring the option of coroutines. The model inside zio is exactly what node.js uses, you can even use zio.ev the same way you would use libuv, it’s just that for most code, coroutine-baed code is actually much easier to read. I’m still thinking of a way to bridge the worlds better, I think zio.select is one way of doing that, I just need to add more primitives that can be awaited on that way and then you could express the promises you were showing here, but that’s way beyond the scope of std.Io.