How Programming Languages Model Asynchronous Program Flow 38 Sharing state between tasks is difficult due to the complexity of ownership rules. Debugging callbacks can be difficult. Coroutines: promises and futures Note! This is another example of M:N threading. Many tasks can run concurrently on one OS thread. Each task is represented as a state machine. Promises in Java Script and futures in Rust are two different implementations that are based on the same idea. There are differences between different implementations, but we'll not focus on those here. It's worth explaining promises a bit since they're widely known due to their use in Java Script. Promises also have a lot in common with Rust's futures. First of all, many languages have a concept of promises, but I'll use the one from Java Script in the following examples. Promises are one way to deal with the complexity that comes with a callback-based approach. Instead of: set Timer(200, () => { set Timer(100, () => { set Timer(50, () => { console. log("I'm the last one"); }); }); }); We can do: function timer(ms) { return new Promise((resolve) => set Timeout(resolve, ms)); } timer(200). then(() => timer(100)). then(() => timer(50)). then(() => console. log("I'm the last one"));