How Programming Languages Model Asynchronous Program Flow 26 Callback based approaches Promises, futures, and async/await Definitions We can broadly categorize abstractions over concurrent operations into two groups: 1. Cooperative : These are tasks that yield voluntarily either by explicitly yielding or by calling a function that suspends the task when it can't progress further before another operation has finished (such as making a network call). Most often, these tasks yield to a scheduler of some sort. Examples of this are tasks generated by async /await in Rust and Java Script. 2. Non-cooperative : Tasks that don't necessarily yield voluntarily. In such a system, the scheduler must be able to pre-empt a running task, meaning that the scheduler can stop the task and take control over the CPU even though the task would have been able to do work and progress. Examples of this are OS threads and Goroutines (after GO version 1. 14). Figure 2. 1-Non-cooperative vs. cooperative multitasking 