Language | Coroutine | Type |
---|---|---|
Python | async/await | Stackless |
Python | yield | Stackless |
JavaScript | async/await | Stackless |
JavaScript | function* (Generators) | Stackless |
C++ | std::coroutine (co_await, co_yield) | Stacked |
C# | async/await | Stackless |
Kotlin | suspend functions | Stackless |
Lua | coroutine.create, coroutine.resume | Stacked |
Ruby | Fiber.new | Stacked |
Node.js | async/await | Stackless |
Go | goroutines | Stackless |
Stacked Coroutines
These are coroutines that rely on the program's call stack for managing their state.
When a coroutine calls another coroutine, the caller's state is saved on the stack, and the callee's state is pushed onto the stack. This allows for a natural nesting of coroutine calls.
Example: Traditional thread-based systems or languages like C++ with stackful coroutines.
Advantages: Easier to implement and debug because they follow the natural call stack. Disadvantages: Limited by the size of the call stack and can be less efficient in terms of memory usage.
Stackless Coroutines
These coroutines do not use the program's call stack to manage their state. Instead, they maintain their own state explicitly.
The state of the coroutine is stored in a separate data structure, and the coroutine's execution is resumed from this state.
Example: Python's asyncio or JavaScript's async/await.
Advantages: More flexible and efficient in terms of memory usage. They can scale better for large numbers of coroutines. Disadvantages: More complex to implement and debug since they don't follow the natural call stack.