Why Python and JavaScript Coroutines Evolved in Parallel: A Comparative Guide
This article compares the evolution of coroutines in Python and JavaScript, explaining their shared goals, core concepts, historical milestones, mature implementations, and practical code examples to help newcomers understand asynchronous programming across both languages.
Introduction
Having little exposure to front‑end development, the author discovered that the history of asynchronous programming in Python and JavaScript is strikingly similar.
Common Goals
Multi‑core CPUs require concurrency despite the single‑threaded origins of many languages.
Simplify code and avoid callback hell by providing keyword support.
Coroutines use fewer resources than threads and switch contexts faster.
What Is a Coroutine?
A coroutine is a function that can be paused at a pause point , resumed later while preserving its arguments and local variables, and relies on an event loop as the foundation of asynchronous programming.
Chaotic History
Python Coroutine Evolution
Python 2.2 introduced generators.
Python 2.5 added the yield keyword.
Python 3.4 brought yield from and the experimental asyncio I/O framework (PEP 3156).
Python 3.5 introduced async / await syntax (PEP 492).
Python 3.6 formalized the asyncio library.
Other coroutine implementations such as Gevent also appeared during this period.
def foo():
print("foo start")
a = yield 1
print("foo a", a)
yield 2
yield 3
print("foo end")
gen = foo()
print(next(gen))
print(gen.send("a"))
print(next(gen))
print(next(foo()))
print(next(foo()))
list(foo())
"""
foo start
1
foo a a
2
3
foo start
1
foo start
1
foo start
foo a None
foo end
"""JavaScript Coroutine Evolution
Synchronous code.
Callback hell in early asynchronous JavaScript.
ES6 introduced Promises, generators (with function* foo(){}) and the yield keyword.
ES7 added async / await, which wraps a generator and returns a Promise.
Promises also use callbacks via .then() and .catch(), turning nested callbacks into a chain of .then() calls for clearer code.
Generators are implemented on top of coroutines.
function* foo() {
console.log("foo start");
a = yield 1;
console.log("foo a", a);
yield 2;
yield 3;
console.log("foo end");
}
const gen = foo();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3Python Coroutine Maturity
Awaitable objects include coroutines, tasks, and Future objects.
Coroutines
Defined with async def.
Calling a coroutine function returns a coroutine object.
Older style coroutines are based on generators.
Tasks
Created with asyncio.create_task() to schedule coroutines.
Tasks run in the event loop and pause when awaiting a Future.
The event loop executes one task at a time, switching to other tasks when a Future is pending.
Future Objects
Bridge low‑level callback code with high‑level async/await code.
When a Future completes, its registered callbacks are invoked, resuming the awaiting coroutine.
Event Loop Implementations
libevent/libev (used by Gevent).
Tornado’s own IOLoop.
picoev (used by meinheld).
uvloop (a high‑performance loop built on libuv, used by Sanic).
Example
import asyncio, time
async def exec():
await asyncio.sleep(2)
print('exec')
async def go():
print(time.time())
await asyncio.gather(exec(), exec())
print(time.time())
if __name__ == '__main__':
asyncio.run(go())JavaScript Coroutine Maturity
Promise Basics
A Promise is a state machine with three states: pending, fulfilled, and rejected.
async/await Sugar
async/ await wrap generators and promises, making asynchronous code look synchronous and improving error handling, branching, and debugging.
JavaScript Event Loop Mechanics
All tasks run on the main thread forming an execution stack.
A task queue holds events once asynchronous operations produce results.
When the stack is empty, the engine processes the task queue.
Micro‑tasks (e.g., Promise callbacks) are processed before macro‑tasks (e.g., setTimeout, I/O) in each loop iteration.
Example
var sleep = function(time) {
console.log("sleep start");
return new Promise(function(resolve, reject) {
setTimeout(function(){ resolve(); }, time);
});
};
async function exec() {
await sleep(2000);
console.log("sleep end");
}
async function go() {
console.log(Date.now());
const c1 = exec();
console.log("-------1");
const c2 = exec();
console.log(c1, c2);
await c1;
console.log("-------2");
await c2;
console.log(c1, c2);
console.log(Date.now());
}
go();Summary and Comparison
Both languages now support async / await, use similar concepts (generators, futures/promises), and rely on an event loop, though the underlying implementations differ: Python’s asyncio offers extensive APIs and fine‑grained control, while JavaScript’s event loop is largely hidden in browsers and Node.js.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
