⚡ async and await
async and await are Python keywords used for asynchronous programming. They allow a program to handle other tasks while waiting for an operation to finish.
🔹 async
async is used to define an asynchronous function.
async def greet():
print("Hello!")Such a function is called a coroutine.
🔹 await
await is used inside an async function to wait for an asynchronous operation to complete.
import asyncio
async def greet():
print("Hello")
await asyncio.sleep(2)
print("Done")
asyncio.run(greet())Hello
DoneThe await pauses this coroutine for 2 seconds, while allowing other asynchronous tasks to run.
<div class="infographic-pyasyncawait">
<style>
.infographic-pyasyncawait {
--ink:#1a1a1a; --ink-soft:#5c5c5c; --paper:#fff; --paper-alt:#f0ece4;
--accent:#2563eb; --accent-soft:#e3ebfd; --line:#ddd6c9;
--ok:#1f9d55; --ok-soft:#e3f5ec;
--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-pyasyncawait[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;
}
.infographic-pyasyncawait *,.infographic-pyasyncawait *::before,.infographic-pyasyncawait *::after{box-sizing:border-box;}
.infographic-pyasyncawait .aw-wrap{padding:0 clamp(1rem,4vw,2rem) 1.5rem;}
.infographic-pyasyncawait 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-pyasyncawait .aw-sub{text-align:center;color:var(--ink-soft);font-size:0.85rem;margin:0 0 1.3rem;}
.infographic-pyasyncawait .aw-tabs{display:flex;justify-content:center;gap:0.35rem;margin-bottom:1.4rem;border-bottom:1px solid var(--line);flex-wrap:wrap;}
.infographic-pyasyncawait .aw-tab-btn{font:inherit;font-family:var(--mono-font);font-size:0.78rem;font-weight:700;background:none;border:none;border-bottom:2.5px solid transparent;color:var(--ink-soft);padding:0.55rem 0.8rem;cursor:pointer;margin-bottom:-1px;}
.infographic-pyasyncawait .aw-tab-btn.is-active{color:var(--accent);border-bottom-color:var(--accent);}
.infographic-pyasyncawait .aw-panel.is-hidden-tab{display:none;}
.infographic-pyasyncawait .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-pyasyncawait .code-block .kw{color:var(--accent);font-weight:700;}
.infographic-pyasyncawait .code-block .aio{font-weight:700;color:#9333ea;}
.infographic-pyasyncawait[data-theme="dark"] .code-block .aio{color:#c58af2;}
.infographic-pyasyncawait .row{display:flex;justify-content:center;gap:0.5rem;margin-bottom:0.8rem;flex-wrap:wrap;}
.infographic-pyasyncawait .btn{font:inherit;font-family:var(--mono-font);font-size:0.8rem;font-weight:700;background:var(--accent);color:#fff;border:none;border-radius:999px;padding:0.48rem 0.9rem;cursor:pointer;}
.infographic-pyasyncawait .btn.secondary{background:var(--paper-alt);color:var(--ink);border:1.5px solid var(--line);border-radius:6px;}
.infographic-pyasyncawait .btn:disabled{opacity:0.5;cursor:default;}
.infographic-pyasyncawait .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-pyasyncawait .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-pyasyncawait .device-bar .dot{width:9px;height:9px;border-radius:50%;background:#5a5a5a;}
.infographic-pyasyncawait .device-bar .dot:nth-child(1){background:#ff5f56;}
.infographic-pyasyncawait .device-body{padding:0.75rem 0.9rem;font-family:var(--mono-font);font-size:0.8rem;color:var(--ed-text);min-height:90px;}
.infographic-pyasyncawait .log-line{line-height:1.7;opacity:0;animation:aw-fadein .25s ease forwards;}
@keyframes aw-fadein{from{opacity:0;}to{opacity:1;}}
.infographic-pyasyncawait .log-empty{color:var(--ed-soft);}
.infographic-pyasyncawait .task-row{display:flex;align-items:center;gap:0.7rem;margin-bottom:0.6rem;}
.infographic-pyasyncawait .task-label{font-family:var(--mono-font);font-size:0.8rem;width:140px;flex:none;color:var(--ink);}
.infographic-pyasyncawait .task-track{flex:1;height:18px;background:var(--paper-alt);border:1px solid var(--line);border-radius:999px;overflow:hidden;}
.infographic-pyasyncawait .task-fill{height:100%;width:0%;background:var(--accent);border-radius:999px;transition:width linear;}
.infographic-pyasyncawait .task-fill.is-done{background:var(--ok);}
.infographic-pyasyncawait .task-time{font-family:var(--mono-font);font-size:0.68rem;color:var(--ink-soft);width:30px;text-align:right;flex:none;}
.infographic-pyasyncawait .sw-row{display:flex;justify-content:center;align-items:center;gap:1rem;margin:0.9rem 0 0.6rem;}
.infographic-pyasyncawait .stopwatch{font-family:var(--display-font);font-size:1.3rem;font-weight:600;color:var(--accent);}
.infographic-pyasyncawait .aw-caption{text-align:center;font-size:0.85rem;color:var(--ink-soft);margin-top:0.6rem;}
@media (prefers-reduced-motion:reduce){.infographic-pyasyncawait *,.infographic-pyasyncawait *::before,.infographic-pyasyncawait *::after{animation-duration:.001ms!important;transition-duration:.001ms!important;}}
</style>
<div class="aw-wrap">
<h1>async / await</h1>
<p class="aw-sub">Two keywords that turn "run this" into "schedule this, and let other things happen while it waits."</p>
<div class="aw-tabs">
<button class="aw-tab-btn is-active" data-tab="coro">Calling ≠ Running</button>
<button class="aw-tab-btn" data-tab="gather">await vs gather()</button>
</div>
<!-- CORO TAB -->
<div class="aw-panel" id="tabCoro">
<div class="code-block"><span class="kw">async def</span> make_coffee():
print("Brewing...")
<span class="kw">await</span> <span class="aio">asyncio.sleep(2)</span>
print("Coffee ready!")
<span class="kw">return</span> "coffee"</div>
<div class="row">
<button class="btn secondary" id="callBtn">Run: make_coffee()</button>
<button class="btn" id="runBtn">Run: asyncio.run(make_coffee())</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="coroLog"><div class="log-empty">Try calling it plain first, then with asyncio.run().</div></div>
</div>
<p class="aw-caption">async def doesn't make a function run when called — it makes it return a coroutine object, inert until something actually drives it.</p>
</div>
<!-- GATHER TAB -->
<div class="aw-panel is-hidden-tab" id="tabGather">
<div class="code-block" id="gatherCode"><span class="kw">async def</span> main():
<span class="kw">await</span> make_coffee() <span style="color:var(--ink-soft);"># 2s</span>
<span class="kw">await</span> make_toast() <span style="color:var(--ink-soft);"># 1.5s</span></div>
<div class="row">
<button class="btn secondary is-selected" data-mode="seq" id="seqBtn">two separate awaits</button>
<button class="btn secondary" data-mode="par" id="parBtn"><span class="aio">asyncio.gather(...)</span></button>
</div>
<div class="task-row">
<span class="task-label">☕ make_coffee()</span>
<div class="task-track"><div class="task-fill" id="fillCoffee"></div></div>
<span class="task-time">2.0s</span>
</div>
<div class="task-row">
<span class="task-label">🍞 make_toast()</span>
<div class="task-track"><div class="task-fill" id="fillToast"></div></div>
<span class="task-time">1.5s</span>
</div>
<div class="sw-row">
<button class="btn" id="gatherRunBtn">▶ Run main()</button>
<span class="stopwatch" id="gatherStopwatch">0.0s</span>
</div>
<p class="aw-caption" id="gatherCaption">Two awaits, one after another: each one pauses main() until it's fully done — 3.5s total.</p>
</div>
</div>
<script>
(function(){
var root = document.currentScript.closest('.infographic-pyasyncawait') || document.querySelector('.infographic-pyasyncawait');
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');
var tabBtns = root.querySelectorAll('.aw-tab-btn');
var panels = { coro: root.querySelector('#tabCoro'), gather: root.querySelector('#tabGather') };
tabBtns.forEach(function (btn) {
btn.addEventListener('click', function () {
tabBtns.forEach(function (b) { b.classList.toggle('is-active', b === btn); });
var t = btn.dataset.tab;
Object.keys(panels).forEach(function (k) { panels[k].classList.toggle('is-hidden-tab', k !== t); });
});
});
/* ---------------- CORO TAB ---------------- */
var callBtn = root.querySelector('#callBtn');
var runBtn = root.querySelector('#runBtn');
var coroLog = root.querySelector('#coroLog');
var firstCoroLog = true;
var coroRunning = false;
function logCoro(text) {
if (firstCoroLog) { coroLog.innerHTML = ''; firstCoroLog = false; }
var line = document.createElement('div');
line.className = 'log-line';
line.textContent = text;
coroLog.appendChild(line);
}
callBtn.addEventListener('click', function () {
logCoro('>>> make_coffee()');
logCoro('<coroutine object make_coffee at 0x7f3a10c8a0>');
logCoro('(nothing printed — the body never ran)');
});
runBtn.addEventListener('click', function () {
if (coroRunning) return;
coroRunning = true;
runBtn.disabled = true;
firstCoroLog = true;
coroLog.innerHTML = '';
logCoro('>>> asyncio.run(make_coffee())');
setTimeout(function () { logCoro('Brewing...'); }, 200);
setTimeout(function () { logCoro('(...waiting on asyncio.sleep(2) — other tasks could run here...)'); }, 700);
setTimeout(function () {
logCoro('Coffee ready!');
logCoro('→ "coffee"');
coroRunning = false;
runBtn.disabled = false;
}, 2200);
});
/* ---------------- GATHER TAB ---------------- */
var seqBtn = root.querySelector('#seqBtn');
var parBtn = root.querySelector('#parBtn');
var gatherCode = root.querySelector('#gatherCode');
var gatherCaption = root.querySelector('#gatherCaption');
var gatherRunBtn = root.querySelector('#gatherRunBtn');
var gatherStopwatch = root.querySelector('#gatherStopwatch');
var fills = { coffee: root.querySelector('#fillCoffee'), toast: root.querySelector('#fillToast') };
var durations = { coffee: 2000, toast: 1500 };
var gatherMode = 'seq';
var gatherRunning = false;
var swInterval = null;
var codes = {
seq: '<span class="kw">async def</span> main():\n <span class="kw">await</span> make_coffee() <span style="color:var(--ink-soft);"># 2s</span>\n <span class="kw">await</span> make_toast() <span style="color:var(--ink-soft);"># 1.5s</span>',
par: '<span class="kw">async def</span> main():\n <span class="kw">await</span> <span class="aio">asyncio.gather(</span>\n make_coffee(), make_toast()\n <span class="aio">)</span>'
};
var gatherCaptions = {
seq: 'Two awaits, one after another: each one pauses main() until it\'s fully done — 3.5s total.',
par: 'asyncio.gather() starts both coroutines together — total time is close to the longer one, about 2s.'
};
function selectMode(btn) {
[seqBtn, parBtn].forEach(function (b) { b.classList.toggle('is-selected', b === btn); });
}
seqBtn.classList.add('is-selected');
seqBtn.addEventListener('click', function () { if (gatherRunning) return; gatherMode = 'seq'; selectMode(seqBtn); gatherCode.innerHTML = codes.seq; gatherCaption.textContent = gatherCaptions.seq; resetGatherBars(); });
parBtn.addEventListener('click', function () { if (gatherRunning) return; gatherMode = 'par'; selectMode(parBtn); gatherCode.innerHTML = codes.par; gatherCaption.textContent = gatherCaptions.par; resetGatherBars(); });
function resetGatherBars() {
Object.keys(fills).forEach(function (k) {
var el = fills[k];
el.style.transition = 'none';
el.style.width = '0%';
el.classList.remove('is-done');
void el.offsetWidth;
el.style.transition = '';
});
gatherStopwatch.textContent = '0.0s';
}
function runGatherTask(key, cb) {
var el = fills[key];
el.style.transitionDuration = (durations[key] / 1000) + 's';
requestAnimationFrame(function () { el.style.width = '100%'; });
setTimeout(function () { el.classList.add('is-done'); if (cb) cb(); }, durations[key]);
}
gatherRunBtn.addEventListener('click', function () {
if (gatherRunning) return;
gatherRunning = true;
gatherRunBtn.disabled = true;
resetGatherBars();
var start = performance.now();
swInterval = setInterval(function () {
gatherStopwatch.textContent = ((performance.now() - start) / 1000).toFixed(1) + 's';
}, 100);
function finish() {
clearInterval(swInterval);
gatherStopwatch.textContent = ((performance.now() - start) / 1000).toFixed(1) + 's';
gatherRunning = false;
gatherRunBtn.disabled = false;
}
if (gatherMode === 'seq') {
runGatherTask('coffee', function () { runGatherTask('toast', finish); });
} else {
var done = 0;
['coffee', 'toast'].forEach(function (key) {
runGatherTask(key, function () { done++; if (done === 2) finish(); });
});
}
});
})();
</script>
</div>🚀 Multiple Tasks
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 tasks can wait concurrently, rather than one completely finishing before the other starts.
asyncsays "this function can run asynchronously."awaitsays "pause here until this async operation is ready, and let other tasks run meanwhile."