๐ Coroutines
A coroutine is a special function that can pause its execution and resume later.
In Python, coroutines are usually created using async def.
๐น Creating a Coroutine
import asyncio
async def greet():
print("Hello")
await asyncio.sleep(2)
print("Done")Calling it:
greet()does not immediately execute the function. It creates a coroutine object.
To actually run it:
asyncio.run(greet())โธ๏ธ How await Works
async def greet():
print("Hello")
await asyncio.sleep(2)
print("Done")๐ Multiple Coroutines
import asyncio
async def task(name):
print(name, "started")
await asyncio.sleep(2)
print(name, "finished")
async def main():
await asyncio.gather(
task("Task 1"),
task("Task 2")
)
asyncio.run(main())Both coroutines can make progress while the other is waiting.
Coroutine = A function that can pause with
awaitand resume later.
<div class="infographic-pycoroutine">
<style>
.infographic-pycoroutine {
--ink:#1a1a1a; --ink-soft:#5c5c5c; --paper:#fff; --paper-alt:#f0ece4;
--accent:#2563eb; --accent-soft:#e3ebfd; --line:#ddd6c9;
--ok:#1f9d55; --ok-soft:#e3f5ec; --warn:#b45309; --warn-soft:#fdf0dc;
--mono-font:'IBM Plex Mono','SFMono-Regular',Menlo,monospace;
--display-font:'Fraunces',Georgia,serif; --body-font:'Inter',system-ui,sans-serif;
box-sizing:border-box; max-width:680px; margin:0 auto; font-family:var(--body-font); color:var(--ink); background:var(--paper);
}
.infographic-pycoroutine[data-theme="dark"] {
--ink:#e8e6e1; --ink-soft:#9a968c; --paper:#121212; --paper-alt:#1e1e26;
--accent:#5b8def; --accent-soft:#202942; --line:#2c313d;
--ok:#6fdc8c; --ok-soft:#16342a; --warn:#e0a458; --warn-soft:#3a2c14;
}
.infographic-pycoroutine *,.infographic-pycoroutine *::before,.infographic-pycoroutine *::after{box-sizing:border-box;}
.infographic-pycoroutine .co-wrap{padding:0 clamp(1rem,4vw,2rem) 1.5rem;}
.infographic-pycoroutine h1{font-family:var(--display-font);font-weight:600;font-size:clamp(1.4rem,5vw,1.9rem);text-align:center;margin:1.5rem 0 0.25rem;letter-spacing:-0.01em;}
.infographic-pycoroutine .co-sub{text-align:center;color:var(--ink-soft);font-size:0.85rem;margin:0 0 1.3rem;}
.infographic-pycoroutine .code-block{font-family:var(--mono-font);font-size:0.78rem;line-height:1.7;white-space:pre;background:var(--paper-alt);border:1px solid var(--line);border-radius:8px;padding:0.8rem 0.95rem;margin-bottom:1rem;}
.infographic-pycoroutine .code-block .kw{color:var(--accent);font-weight:700;}
.infographic-pycoroutine .code-block .aio{font-weight:700;color:#9333ea;}
.infographic-pycoroutine[data-theme="dark"] .code-block .aio{color:#c58af2;}
.infographic-pycoroutine .run-row{display:flex;justify-content:center;gap:0.5rem;margin-bottom:0.9rem;}
.infographic-pycoroutine .btn{font:inherit;font-family:var(--mono-font);font-size:0.82rem;font-weight:700;background:var(--accent);color:#fff;border:none;border-radius:999px;padding:0.5rem 1.1rem;cursor:pointer;}
.infographic-pycoroutine .btn.secondary{background:var(--paper-alt);color:var(--ink);border:1.5px solid var(--line);border-radius:6px;}
.infographic-pycoroutine .btn:disabled{opacity:0.5;cursor:default;}
.infographic-pycoroutine .task-add-row{display:flex;justify-content:center;gap:0.4rem;margin-bottom:0.7rem;flex-wrap:wrap;align-items:center;}
.infographic-pycoroutine .task-add-row input{font-family:var(--mono-font);font-size:0.82rem;padding:0.4rem 0.55rem;border:1.5px solid var(--line);border-radius:5px;background:var(--paper-alt);color:var(--ink);}
.infographic-pycoroutine .task-add-row input:focus{outline:none;border-color:var(--accent);}
#taskNameInput{width:110px;}
#taskDurInput{width:70px;text-align:center;}
.infographic-pycoroutine .task-chip-row{display:flex;flex-wrap:wrap;justify-content:center;gap:0.4rem;margin-bottom:0.9rem;min-height:36px;align-items:center;}
.infographic-pycoroutine .task-chip{font-family:var(--mono-font);font-size:0.76rem;font-weight:700;padding:0.3rem 0.6rem;border-radius:999px;border:1.5px solid var(--accent);background:var(--accent-soft);color:var(--accent);}
.infographic-pycoroutine .task-chip button{background:none;border:none;color:inherit;font-weight:700;cursor:pointer;margin-left:0.3rem;opacity:0.6;}
.infographic-pycoroutine .task-chip button:hover{opacity:1;}
.infographic-pycoroutine .device{--ed-bg:#1e1e1e;--ed-bar:#2d2d2d;--ed-text:#d4d4d4;--ed-soft:#8a8a8a;--ed-ok:#6fdc8c;background:var(--ed-bg);border:1px solid var(--line);border-radius:8px;overflow:hidden;}
.infographic-pycoroutine .device-bar{display:flex;align-items:center;gap:0.4rem;background:var(--ed-bar);padding:0.5rem 0.7rem;border-bottom:1px solid #3a3a3a;font-size:0.72rem;color:var(--ed-soft);font-weight:600;}
.infographic-pycoroutine .device-bar .dot{width:9px;height:9px;border-radius:50%;background:#5a5a5a;}
.infographic-pycoroutine .device-bar .dot:nth-child(1){background:#ff5f56;}
.infographic-pycoroutine .device-body{padding:0.75rem 0.9rem;font-family:var(--mono-font);font-size:0.78rem;color:var(--ed-text);min-height:80px;max-height:200px;overflow-y:auto;}
.infographic-pycoroutine .log-line{line-height:1.65;opacity:0;animation:co-fadein .2s ease forwards;}
@keyframes co-fadein{from{opacity:0;}to{opacity:1;}}
.infographic-pycoroutine .log-empty{color:var(--ed-soft);}
.infographic-pycoroutine .co-caption{text-align:center;font-size:0.85rem;color:var(--ink-soft);margin-top:0.9rem;}
@media (prefers-reduced-motion:reduce){.infographic-pycoroutine *,.infographic-pycoroutine *::before,.infographic-pycoroutine *::after{animation-duration:.001ms!important;transition-duration:.001ms!important;}}
</style>
<div class="co-wrap">
<h1>Coroutines</h1>
<p class="co-sub">A function that can pause mid-execution โ and let something else run while it waits.</p>
<div class="code-block"><span class="kw">async def</span> task(name):
print(name, "started")
<span class="kw">await</span> <span class="aio">asyncio.sleep(</span>seconds<span class="aio">)</span>
print(name, "finished")
<span class="kw">async def</span> main():
<span class="kw">await</span> <span class="aio">asyncio.gather(</span>*tasks<span class="aio">)</span></div>
<div class="task-add-row">
<input id="taskNameInput" type="text" value="Task 3" placeholder="name" />
<input id="taskDurInput" type="text" value="1" placeholder="seconds" />
<button class="btn secondary" id="addTaskBtn">+ add task</button>
</div>
<div class="task-chip-row" id="taskChipRow"></div>
<div class="run-row"><button class="btn" id="gatherRunBtn">โถ Run: asyncio.gather(...)</button></div>
<div class="device">
<div class="device-bar"><span class="dot"></span><span class="dot"></span><span class="dot"></span> Terminal</div>
<div class="device-body" id="coLog"><div class="log-empty">Add a couple of tasks, then run them together.</div></div>
</div>
<p class="co-caption">Watch the order: every "started" line prints before any "finished" line โ each coroutine pauses at its own await almost immediately, so they all get a turn before any of them completes.</p>
</div>
<script>
(function(){
var root = document.currentScript.closest('.infographic-pycoroutine') || document.querySelector('.infographic-pycoroutine');
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark = bodyAttr === 'true' ? true : bodyAttr === 'false' ? false : (document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/) || [])[1] === 'true';
if (dark) root.setAttribute('data-theme', 'dark');
/* ---------------- multiple named coroutines via gather ---------------- */
var taskNameInput = root.querySelector('#taskNameInput');
var taskDurInput = root.querySelector('#taskDurInput');
var addTaskBtn = root.querySelector('#addTaskBtn');
var taskChipRow = root.querySelector('#taskChipRow');
var gatherRunBtn = root.querySelector('#gatherRunBtn');
var coLog = root.querySelector('#coLog');
var firstLog = true;
var tasks = [
{ name: 'Task 1', dur: 2 },
{ name: 'Task 2', dur: 1.5 }
];
function renderTasks() {
taskChipRow.innerHTML = '';
tasks.forEach(function (t, i) {
var chip = document.createElement('span');
chip.className = 'task-chip';
chip.innerHTML = t.name + ' (' + t.dur + 's) <button data-i="' + i + '">ร</button>';
taskChipRow.appendChild(chip);
});
taskChipRow.querySelectorAll('button').forEach(function (btn) {
btn.addEventListener('click', function () {
tasks.splice(parseInt(btn.dataset.i, 10), 1);
renderTasks();
});
});
}
renderTasks();
addTaskBtn.addEventListener('click', function () {
if (tasks.length >= 5) return;
var name = taskNameInput.value.trim() || ('Task ' + (tasks.length + 1));
var dur = parseFloat(taskDurInput.value);
if (isNaN(dur) || dur <= 0) dur = 1;
tasks.push({ name: name, dur: dur });
renderTasks();
taskNameInput.value = 'Task ' + (tasks.length + 1);
});
function logCo(text) {
if (firstLog) { coLog.innerHTML = ''; firstLog = false; }
var line = document.createElement('div');
line.className = 'log-line';
line.textContent = text;
coLog.appendChild(line);
coLog.scrollTop = coLog.scrollHeight;
}
var gatherRunning = false;
gatherRunBtn.addEventListener('click', function () {
if (gatherRunning || tasks.length === 0) return;
gatherRunning = true;
gatherRunBtn.disabled = true;
firstLog = true;
coLog.innerHTML = '';
// all "started" prints happen right away (each coroutine runs to its first await)
tasks.forEach(function (t, i) {
setTimeout(function () { logCo(t.name + ' started'); }, i * 80);
});
// "finished" prints happen in order of duration, after all starts
var sorted = tasks.slice().sort(function (a, b) { return a.dur - b.dur; });
var baseDelay = tasks.length * 80 + 200;
sorted.forEach(function (t, i) {
setTimeout(function () {
logCo(t.name + ' finished');
if (i === sorted.length - 1) { gatherRunning = false; gatherRunBtn.disabled = false; }
}, baseDelay + t.dur * 1000);
});
});
})();
</script>
</div>