feat(assessment): auto comprehensive assessment - rules engine from college scoring standard, per-semester stats, manual overrides, word export
This commit is contained in:
@@ -0,0 +1,937 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" data-content_root="../">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta property="og:title" content="A conceptual overview of asyncio" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://docs.python.org/3/howto/a-conceptual-overview-of-asyncio.html" />
|
||||
<meta property="og:site_name" content="Python documentation" />
|
||||
<meta property="og:description" content="This HOWTO article seeks to help you build a sturdy mental model of how asyncio fundamentally works, helping you understand the how and why behind the recommended patterns. You might be curious abo..." />
|
||||
<meta property="og:image" content="_static/og-image.png" />
|
||||
<meta property="og:image:alt" content="Python documentation" />
|
||||
<meta name="description" content="This HOWTO article seeks to help you build a sturdy mental model of how asyncio fundamentally works, helping you understand the how and why behind the recommended patterns. You might be curious abo..." />
|
||||
<meta name="theme-color" content="#3776ab">
|
||||
<meta property="og:image:width" content="200">
|
||||
<meta property="og:image:height" content="200">
|
||||
|
||||
<title>A conceptual overview of asyncio — Python 3.15.0 documentation</title><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css?v=b86133f3" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/classic.css?v=234b1a7c" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pydoctheme.css?v=48df1187" />
|
||||
<link id="pygments_dark_css" media="(prefers-color-scheme: dark)" rel="stylesheet" type="text/css" href="../_static/pygments_dark.css?v=0fc419ee" />
|
||||
|
||||
<script src="../_static/documentation_options.js?v=0fe728b1"></script>
|
||||
<script src="../_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
|
||||
<script src="../_static/sidebar.js"></script>
|
||||
|
||||
<link rel="search" type="application/opensearchdescription+xml"
|
||||
title="Search within Python 3.15.0 documentation"
|
||||
href="../_static/opensearch.xml"/>
|
||||
<link rel="author" title="About these documents" href="../about.html" />
|
||||
<link rel="index" title="Index" href="../genindex.html" />
|
||||
<link rel="search" title="Search" href="../search.html" />
|
||||
<link rel="copyright" title="Copyright" href="../copyright.html" />
|
||||
<link rel="next" title="Porting Extension Modules to Python 3" href="cporting.html" />
|
||||
<link rel="prev" title="Python HOWTOs" href="index.html" />
|
||||
|
||||
|
||||
<link rel="canonical" href="https://docs.python.org/3/howto/a-conceptual-overview-of-asyncio.html">
|
||||
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
@media only screen {
|
||||
table.full-width-table {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="../_static/pydoctheme_dark.css" media="(prefers-color-scheme: dark)" id="pydoctheme_dark_css">
|
||||
<link rel="shortcut icon" type="image/png" href="../_static/py.svg">
|
||||
<script type="text/javascript" src="../_static/copybutton.js"></script>
|
||||
<script type="text/javascript" src="../_static/menu.js"></script>
|
||||
<script type="text/javascript" src="../_static/search-focus.js"></script>
|
||||
<script type="text/javascript" src="../_static/themetoggle.js"></script>
|
||||
<script type="text/javascript" src="../_static/rtd_switcher.js"></script>
|
||||
<meta name="readthedocs-addons-api-version" content="1">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="mobile-nav">
|
||||
<input type="checkbox" id="menuToggler" class="toggler__input" aria-controls="navigation"
|
||||
aria-pressed="false" aria-expanded="false" role="button" aria-label="Menu">
|
||||
<nav class="nav-content" role="navigation">
|
||||
<label for="menuToggler" class="toggler__label">
|
||||
<span></span>
|
||||
</label>
|
||||
<span class="nav-items-wrapper">
|
||||
<a href="https://www.python.org/" class="nav-logo">
|
||||
<img src="../_static/py.svg" alt="Python logo">
|
||||
</a>
|
||||
<span class="version_switcher_placeholder"></span>
|
||||
<form role="search" class="search" action="../search.html" method="get">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="search-icon">
|
||||
<path fill-rule="nonzero" fill="currentColor" d="M15.5 14h-.79l-.28-.27a6.5 6.5 0 001.48-5.34c-.47-2.78-2.79-5-5.59-5.34a6.505 6.505 0 00-7.27 7.27c.34 2.8 2.56 5.12 5.34 5.59a6.5 6.5 0 005.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path>
|
||||
</svg>
|
||||
<input placeholder="Quick search" aria-label="Quick search" type="search" name="q">
|
||||
<input type="submit" value="Go">
|
||||
</form>
|
||||
</span>
|
||||
</nav>
|
||||
<div class="menu-wrapper">
|
||||
<nav class="menu" role="navigation" aria-label="main navigation">
|
||||
<div class="language_switcher_placeholder"></div>
|
||||
|
||||
<label class="theme-selector-label">
|
||||
Theme
|
||||
<select class="theme-selector" oninput="activateTheme(this.value)">
|
||||
<option value="auto" selected>Auto</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="dark">Dark</option>
|
||||
</select>
|
||||
</label>
|
||||
<div>
|
||||
<h3><a href="../contents.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">A conceptual overview of <code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code></a><ul>
|
||||
<li><a class="reference internal" href="#a-conceptual-overview-part-1-the-high-level">A conceptual overview part 1: the high-level</a><ul>
|
||||
<li><a class="reference internal" href="#event-loop">Event loop</a></li>
|
||||
<li><a class="reference internal" href="#asynchronous-functions-and-coroutines">Asynchronous functions and coroutines</a></li>
|
||||
<li><a class="reference internal" href="#tasks">Tasks</a></li>
|
||||
<li><a class="reference internal" href="#await">await</a><ul>
|
||||
<li><a class="reference internal" href="#awaiting-tasks">Awaiting tasks</a></li>
|
||||
<li><a class="reference internal" href="#awaiting-coroutines">Awaiting coroutines</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#a-conceptual-overview-part-2-the-nuts-and-bolts">A conceptual overview part 2: the nuts and bolts</a><ul>
|
||||
<li><a class="reference internal" href="#the-inner-workings-of-coroutines">The inner workings of coroutines</a></li>
|
||||
<li><a class="reference internal" href="#futures">Futures</a></li>
|
||||
<li><a class="reference internal" href="#a-homemade-asyncio-sleep">A homemade asyncio.sleep</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="index.html"
|
||||
title="previous chapter">Python HOWTOs</a></p>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="cporting.html"
|
||||
title="next chapter">Porting Extension Modules to Python 3</a></p>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const title = document.querySelector('meta[property="og:title"]').content;
|
||||
const elements = document.querySelectorAll('.improvepage');
|
||||
const pageurl = window.location.href.split('?')[0];
|
||||
elements.forEach(element => {
|
||||
const url = new URL(element.href.split('?')[0].replace("-nojs", ""));
|
||||
url.searchParams.set('pagetitle', title);
|
||||
url.searchParams.set('pageurl', pageurl);
|
||||
url.searchParams.set('pagesource', "howto/a-conceptual-overview-of-asyncio.rst");
|
||||
element.href = url.toString();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div role="note" aria-label="source link">
|
||||
<h3>This page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../bugs.html">Report a bug</a></li>
|
||||
<li><a class="improvepage" href="../improve-page-nojs.html">Improve this page</a></li>
|
||||
<li>
|
||||
<a href="https://github.com/python/cpython/blob/3.15/Doc/howto/a-conceptual-overview-of-asyncio.rst?plain=1"
|
||||
rel="nofollow">Show source
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="Related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="cporting.html" title="Porting Extension Modules to Python 3"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="index.html" title="Python HOWTOs"
|
||||
accesskey="P">previous</a> |</li>
|
||||
|
||||
<li class="root-icon"><img src="../_static/py.svg" alt="Python logo" style="vertical-align: middle; margin-top: -1px"></li>
|
||||
<li><a href="https://www.python.org/">Python</a> »</li>
|
||||
<li class="switchers">
|
||||
<div class="language_switcher_placeholder"></div>
|
||||
<div class="version_switcher_placeholder"></div>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
</li>
|
||||
<li id="cpython-language-and-version">
|
||||
<a href="../index.html">3.15.0 Documentation</a> »
|
||||
</li>
|
||||
|
||||
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Python HOWTOs</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">A conceptual overview of <code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code></a></li>
|
||||
<li class="right">
|
||||
|
||||
|
||||
<div class="inline-search" role="search">
|
||||
<form class="inline-search" action="../search.html" method="get">
|
||||
<input placeholder="Quick search" aria-label="Quick search" type="search" name="q" id="search-box">
|
||||
<input type="submit" value="Go">
|
||||
</form>
|
||||
</div>
|
||||
|
|
||||
</li>
|
||||
<li class="right">
|
||||
<label class="theme-selector-label">
|
||||
Theme
|
||||
<select class="theme-selector" oninput="activateTheme(this.value)">
|
||||
<option value="auto" selected>Auto</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="dark">Dark</option>
|
||||
</select>
|
||||
</label> |</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section id="a-conceptual-overview-of-asyncio">
|
||||
<span id="id1"></span><h1>A conceptual overview of <code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code><a class="headerlink" href="#a-conceptual-overview-of-asyncio" title="Link to this heading">¶</a></h1>
|
||||
<p>This <a class="reference internal" href="index.html#how-tos"><span class="std std-ref">HOWTO</span></a> article seeks to help you build a sturdy mental
|
||||
model of how <a class="reference internal" href="../library/asyncio.html#module-asyncio" title="asyncio: Asynchronous I/O."><code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code></a> fundamentally works, helping you understand the
|
||||
how and why behind the recommended patterns.</p>
|
||||
<p>You might be curious about some key <code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code> concepts.
|
||||
By the end of this article, you’ll be able to comfortably answer these questions:</p>
|
||||
<ul class="simple">
|
||||
<li><p>What’s happening behind the scenes when an object is awaited?</p></li>
|
||||
<li><p>How does <code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code> differentiate between a task which doesn’t need
|
||||
CPU time (such as a network request or file read) as opposed to a task that
|
||||
does (such as computing n-factorial)?</p></li>
|
||||
<li><p>How to write an asynchronous variant of an operation, such as
|
||||
an async sleep or database request.</p></li>
|
||||
</ul>
|
||||
<div class="admonition seealso">
|
||||
<p class="admonition-title">See also</p>
|
||||
<ul class="simple">
|
||||
<li><p>The <a class="reference external" href="https://github.com/anordin95/a-conceptual-overview-of-asyncio/tree/main">guide</a> that inspired this HOWTO article, by Alexander Nordin.</p></li>
|
||||
<li><p>This in-depth <a class="reference external" href="https://www.youtube.com/watch?v=Xbl7XjFYsN4&list=PLhNSoGM2ik6SIkVGXWBwerucXjgP1rHmB">YouTube tutorial series</a> on
|
||||
<code class="docutils literal notranslate"><span class="pre">asyncio</span></code> created by Python core team member, Łukasz Langa.</p></li>
|
||||
<li><p><a class="reference external" href="https://aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html">500 Lines or Less: A Web Crawler With asyncio Coroutines</a> by A.
|
||||
Jesse Jiryu Davis and Guido van Rossum.</p></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section id="a-conceptual-overview-part-1-the-high-level">
|
||||
<h2>A conceptual overview part 1: the high-level<a class="headerlink" href="#a-conceptual-overview-part-1-the-high-level" title="Link to this heading">¶</a></h2>
|
||||
<p>In part 1, we’ll cover the main, high-level building blocks of <code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code>:
|
||||
the event loop, coroutine functions, coroutine objects, tasks, and <code class="docutils literal notranslate"><span class="pre">await</span></code>.</p>
|
||||
<section id="event-loop">
|
||||
<h3>Event loop<a class="headerlink" href="#event-loop" title="Link to this heading">¶</a></h3>
|
||||
<p>Everything in <code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code> happens relative to the event loop.
|
||||
It’s the star of the show, but prefers to work behind the scenes, managing
|
||||
and coordinating resources.
|
||||
It’s like an orchestra conductor.
|
||||
Some power is explicitly granted to it, but a lot of its ability to get things
|
||||
done comes from the respect and cooperation of its band members.</p>
|
||||
<p>In more technical terms, the event loop contains a collection of jobs to be run.
|
||||
Some jobs are added directly by you, and some indirectly by <code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code>.
|
||||
The event loop takes a job from its backlog of work and invokes it (or “gives
|
||||
it control”), similar to calling a function, and then that job runs.
|
||||
Once it pauses or completes, it returns control to the event loop.
|
||||
The event loop will then select another job from its pool and invoke it.
|
||||
You can <em>roughly</em> think of the collection of jobs as a queue: jobs are added and
|
||||
then processed one at a time, generally (but not always) in order.
|
||||
This process repeats indefinitely, with the event loop cycling endlessly
|
||||
onwards.
|
||||
If there are no more jobs pending execution, the event loop is smart enough to
|
||||
rest and avoid needlessly wasting CPU cycles, and will come back when there’s
|
||||
more work to be done, such as when I/O operations complete or timers expire.</p>
|
||||
<p>Effective execution relies on jobs sharing well and cooperating; a greedy job
|
||||
could hog control and leave the other jobs to starve, rendering the overall
|
||||
event loop approach rather useless.</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">asyncio</span>
|
||||
|
||||
<span class="c1"># This creates an event loop and indefinitely cycles through</span>
|
||||
<span class="c1"># its collection of jobs.</span>
|
||||
<span class="n">event_loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">new_event_loop</span><span class="p">()</span>
|
||||
<span class="n">event_loop</span><span class="o">.</span><span class="n">run_forever</span><span class="p">()</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="asynchronous-functions-and-coroutines">
|
||||
<h3>Asynchronous functions and coroutines<a class="headerlink" href="#asynchronous-functions-and-coroutines" title="Link to this heading">¶</a></h3>
|
||||
<p>This is a basic, boring Python function:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">hello_printer</span><span class="p">():</span>
|
||||
<span class="nb">print</span><span class="p">(</span>
|
||||
<span class="s2">"Hi, I am a lowly, simple printer, though I have all I "</span>
|
||||
<span class="s2">"need in life -- </span><span class="se">\n</span><span class="s2">fresh paper and my dearly beloved octopus "</span>
|
||||
<span class="s2">"partner in crime."</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Calling a regular function invokes its logic or body:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">hello_printer</span><span class="p">()</span>
|
||||
<span class="go">Hi, I am a lowly, simple printer, though I have all I need in life --</span>
|
||||
<span class="go">fresh paper and my dearly beloved octopus partner in crime.</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <a class="reference internal" href="../reference/compound_stmts.html#async-def"><span class="std std-ref">async def</span></a>, as opposed to just a plain <code class="docutils literal notranslate"><span class="pre">def</span></code>, makes
|
||||
this an asynchronous function (or “coroutine function”).
|
||||
Calling it creates and returns a <a class="reference internal" href="../library/asyncio-task.html#coroutine"><span class="std std-ref">coroutine</span></a> object.</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">loudmouth_penguin</span><span class="p">(</span><span class="n">magic_number</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
|
||||
<span class="nb">print</span><span class="p">(</span>
|
||||
<span class="s2">"I am a super special talking penguin. Far cooler than that printer. "</span>
|
||||
<span class="sa">f</span><span class="s2">"By the way, my lucky number is: </span><span class="si">{</span><span class="n">magic_number</span><span class="si">}</span><span class="s2">."</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Calling the async function, <code class="docutils literal notranslate"><span class="pre">loudmouth_penguin</span></code>, does not execute the print statement;
|
||||
instead, it creates a coroutine object:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">loudmouth_penguin</span><span class="p">(</span><span class="n">magic_number</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
|
||||
<span class="go"><coroutine object loudmouth_penguin at 0x104ed2740></span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The terms “coroutine function” and “coroutine object” are often conflated
|
||||
as coroutine.
|
||||
That can be confusing!
|
||||
In this article, coroutine specifically refers to a coroutine object, or more
|
||||
precisely, an instance of <a class="reference internal" href="../library/types.html#types.CoroutineType" title="types.CoroutineType"><code class="xref py py-class docutils literal notranslate"><span class="pre">types.CoroutineType</span></code></a> (native coroutine).
|
||||
Note that coroutines can also exist as instances of
|
||||
<a class="reference internal" href="../library/collections.abc.html#collections.abc.Coroutine" title="collections.abc.Coroutine"><code class="xref py py-class docutils literal notranslate"><span class="pre">collections.abc.Coroutine</span></code></a> – a distinction that matters for type
|
||||
checking.</p>
|
||||
<p>A coroutine represents the function’s body or logic.
|
||||
A coroutine has to be explicitly started; again, merely creating the coroutine
|
||||
does not start it.
|
||||
Notably, the coroutine can be paused and resumed at various points within the
|
||||
function’s body.
|
||||
That pausing and resuming ability is what allows for asynchronous behavior!</p>
|
||||
<p>Coroutines and coroutine functions were built by leveraging the functionality
|
||||
of <a class="reference internal" href="../glossary.html#term-generator-iterator"><span class="xref std std-term">generators</span></a> and
|
||||
<a class="reference internal" href="../glossary.html#term-generator"><span class="xref std std-term">generator functions</span></a>.
|
||||
Recall, a generator function is a function that <a class="reference internal" href="../reference/simple_stmts.html#yield"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">yield</span></code></a>s, like this
|
||||
one:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">get_random_number</span><span class="p">():</span>
|
||||
<span class="c1"># This would be a bad random number generator!</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Hi"</span><span class="p">)</span>
|
||||
<span class="k">yield</span> <span class="mi">1</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Hello"</span><span class="p">)</span>
|
||||
<span class="k">yield</span> <span class="mi">7</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Howdy"</span><span class="p">)</span>
|
||||
<span class="k">yield</span> <span class="mi">4</span>
|
||||
<span class="o">...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Similar to a coroutine function, calling a generator function does not run it.
|
||||
Instead, it creates a generator object:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">get_random_number</span><span class="p">()</span>
|
||||
<span class="go"><generator object get_random_number at 0x1048671c0></span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can proceed to the next <code class="docutils literal notranslate"><span class="pre">yield</span></code> of a generator by using the
|
||||
built-in function <a class="reference internal" href="../builtins/functions.html#next" title="next"><code class="xref py py-func docutils literal notranslate"><span class="pre">next()</span></code></a>.
|
||||
In other words, the generator runs, then pauses.
|
||||
For example:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">generator</span> <span class="o">=</span> <span class="n">get_random_number</span><span class="p">()</span>
|
||||
<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">generator</span><span class="p">)</span>
|
||||
<span class="go">Hi</span>
|
||||
<span class="go">1</span>
|
||||
<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">generator</span><span class="p">)</span>
|
||||
<span class="go">Hello</span>
|
||||
<span class="go">7</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="tasks">
|
||||
<h3>Tasks<a class="headerlink" href="#tasks" title="Link to this heading">¶</a></h3>
|
||||
<p>Roughly speaking, <a class="reference internal" href="../library/asyncio-task.html#asyncio-task-obj"><span class="std std-ref">tasks</span></a> are coroutines (not coroutine
|
||||
functions) tied to an event loop.
|
||||
A task also maintains a list of callback functions whose importance will become
|
||||
clear in a moment when we discuss <a class="reference internal" href="../reference/expressions.html#await"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">await</span></code></a>.</p>
|
||||
<p>Creating a task automatically schedules it for execution (by adding a
|
||||
callback to run it in the event loop’s to-do list, that is, collection of jobs).
|
||||
The recommended way to create tasks is via <a class="reference internal" href="../library/asyncio-task.html#asyncio.create_task" title="asyncio.create_task"><code class="xref py py-func docutils literal notranslate"><span class="pre">asyncio.create_task()</span></code></a>.</p>
|
||||
<p><code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code> automatically associates tasks with the event loop for you.
|
||||
This automatic association was purposely designed into <code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code> for
|
||||
the sake of simplicity.
|
||||
Without it, you’d have to keep track of the event loop object and pass it to
|
||||
any coroutine function that wants to create tasks, adding redundant clutter
|
||||
to your code.</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">coroutine</span> <span class="o">=</span> <span class="n">loudmouth_penguin</span><span class="p">(</span><span class="n">magic_number</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>
|
||||
<span class="c1"># This creates a Task object and schedules its execution via the event loop.</span>
|
||||
<span class="n">task</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">coroutine</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Earlier, we manually created the event loop and set it to run forever.
|
||||
In practice, it’s recommended to use (and common to see) <a class="reference internal" href="../library/asyncio-runner.html#asyncio.run" title="asyncio.run"><code class="xref py py-func docutils literal notranslate"><span class="pre">asyncio.run()</span></code></a>,
|
||||
which takes care of managing the event loop and ensuring the provided
|
||||
coroutine finishes before advancing.
|
||||
For example, many async programs follow this setup:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">asyncio</span>
|
||||
|
||||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">main</span><span class="p">():</span>
|
||||
<span class="c1"># Perform all sorts of wacky, wild asynchronous things...</span>
|
||||
<span class="o">...</span>
|
||||
|
||||
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
|
||||
<span class="n">asyncio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
|
||||
<span class="c1"># The program will not reach the following print statement until the</span>
|
||||
<span class="c1"># coroutine main() finishes.</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="s2">"coroutine main() is done!"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>It’s important to be aware that the task itself is not added to the event loop,
|
||||
only a callback to the task is.
|
||||
This matters if the task object you created is garbage collected before it’s
|
||||
called by the event loop.
|
||||
For example, consider this program:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">hello</span><span class="p">():</span>
|
||||
<span class="linenos"> 2</span> <span class="nb">print</span><span class="p">(</span><span class="s2">"hello!"</span><span class="p">)</span>
|
||||
<span class="linenos"> 3</span>
|
||||
<span class="linenos"> 4</span><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">main</span><span class="p">():</span>
|
||||
<span class="linenos"> 5</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">hello</span><span class="p">())</span>
|
||||
<span class="linenos"> 6</span> <span class="c1"># Other asynchronous instructions which run for a while</span>
|
||||
<span class="linenos"> 7</span> <span class="c1"># and cede control to the event loop...</span>
|
||||
<span class="linenos"> 8</span> <span class="o">...</span>
|
||||
<span class="linenos"> 9</span>
|
||||
<span class="linenos">10</span><span class="n">asyncio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Because there’s no reference to the task object created on line 5, it <em>might</em>
|
||||
be garbage collected before the event loop invokes it.
|
||||
Later instructions in the coroutine <code class="docutils literal notranslate"><span class="pre">main()</span></code> hand control back to the event
|
||||
loop so it can invoke other jobs.
|
||||
When the event loop eventually tries to run the task, it might fail and
|
||||
discover the task object does not exist!
|
||||
This can also happen even if a coroutine keeps a reference to a task but
|
||||
completes before that task finishes.
|
||||
When the coroutine exits, local variables go out of scope and may be subject
|
||||
to garbage collection.
|
||||
In practice, <code class="docutils literal notranslate"><span class="pre">asyncio</span></code> and Python’s garbage collector work pretty hard to
|
||||
ensure this sort of thing doesn’t happen.
|
||||
But that’s no reason to be reckless!</p>
|
||||
</section>
|
||||
<section id="await">
|
||||
<h3>await<a class="headerlink" href="#await" title="Link to this heading">¶</a></h3>
|
||||
<p><a class="reference internal" href="../reference/expressions.html#await"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">await</span></code></a> is a Python keyword that’s commonly used in one of two
|
||||
different ways:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">await</span> <span class="n">task</span>
|
||||
<span class="k">await</span> <span class="n">coroutine</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In a crucial way, the behavior of <code class="docutils literal notranslate"><span class="pre">await</span></code> depends on the type of object
|
||||
being awaited.</p>
|
||||
<section id="awaiting-tasks">
|
||||
<h4>Awaiting tasks<a class="headerlink" href="#awaiting-tasks" title="Link to this heading">¶</a></h4>
|
||||
<p>Awaiting a task will cede control from the current task or coroutine to
|
||||
the event loop.
|
||||
In the process of relinquishing control, a few important things happen.
|
||||
We’ll use the following code example to illustrate:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">plant_a_tree</span><span class="p">():</span>
|
||||
<span class="n">dig_the_hole_task</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">dig_the_hole</span><span class="p">())</span>
|
||||
<span class="k">await</span> <span class="n">dig_the_hole_task</span>
|
||||
|
||||
<span class="c1"># Other instructions associated with planting a tree.</span>
|
||||
<span class="o">...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In this example, imagine the event loop has passed control to the start of the
|
||||
coroutine <code class="docutils literal notranslate"><span class="pre">plant_a_tree()</span></code>.
|
||||
As seen above, the coroutine creates a task and then awaits it.
|
||||
The <code class="docutils literal notranslate"><span class="pre">await</span> <span class="pre">dig_the_hole_task</span></code> instruction adds a callback (which will resume
|
||||
<code class="docutils literal notranslate"><span class="pre">plant_a_tree()</span></code>) to the <code class="docutils literal notranslate"><span class="pre">dig_the_hole_task</span></code> object’s list of callbacks.
|
||||
And then, the instruction cedes control to the event loop.
|
||||
Some time later, the event loop will pass control to <code class="docutils literal notranslate"><span class="pre">dig_the_hole_task</span></code>
|
||||
and the task will finish whatever it needs to do.
|
||||
Once the task finishes, it will add its various callbacks to the event loop,
|
||||
in this case, a call to resume <code class="docutils literal notranslate"><span class="pre">plant_a_tree()</span></code>.</p>
|
||||
<p>Generally speaking, when the awaited task finishes (<code class="docutils literal notranslate"><span class="pre">dig_the_hole_task</span></code>),
|
||||
the original task or coroutine (<code class="docutils literal notranslate"><span class="pre">plant_a_tree()</span></code>) is added back to the event
|
||||
loop’s to-do list to be resumed.</p>
|
||||
<p>This is a basic, yet reliable mental model.
|
||||
In practice, the control handoffs are slightly more complex, but not by much.
|
||||
In part 2, we’ll walk through the details that make this possible.</p>
|
||||
</section>
|
||||
<section id="awaiting-coroutines">
|
||||
<h4>Awaiting coroutines<a class="headerlink" href="#awaiting-coroutines" title="Link to this heading">¶</a></h4>
|
||||
<p><strong>Unlike tasks, awaiting a coroutine does not hand control back to the event
|
||||
loop!</strong>
|
||||
Wrapping a coroutine in a task first, then awaiting that would cede
|
||||
control.
|
||||
The behavior of <code class="docutils literal notranslate"><span class="pre">await</span> <span class="pre">coroutine</span></code> is effectively the same as invoking a
|
||||
regular, synchronous Python function.
|
||||
Consider this program:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">asyncio</span>
|
||||
|
||||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">coro_a</span><span class="p">():</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="s2">"I am coro_a(). Hi!"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">coro_b</span><span class="p">():</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="s2">"I am coro_b(). I sure hope no one hogs the event loop..."</span><span class="p">)</span>
|
||||
|
||||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">main</span><span class="p">():</span>
|
||||
<span class="n">task_b</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">coro_b</span><span class="p">())</span>
|
||||
<span class="n">num_repeats</span> <span class="o">=</span> <span class="mi">3</span>
|
||||
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_repeats</span><span class="p">):</span>
|
||||
<span class="k">await</span> <span class="n">coro_a</span><span class="p">()</span>
|
||||
<span class="k">await</span> <span class="n">task_b</span>
|
||||
|
||||
<span class="n">asyncio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The first statement in the coroutine <code class="docutils literal notranslate"><span class="pre">main()</span></code> creates <code class="docutils literal notranslate"><span class="pre">task_b</span></code> and schedules
|
||||
it for execution via the event loop.
|
||||
Then, <code class="docutils literal notranslate"><span class="pre">coro_a()</span></code> is repeatedly awaited. Control never cedes to the
|
||||
event loop, which is why we see the output of all three <code class="docutils literal notranslate"><span class="pre">coro_a()</span></code>
|
||||
invocations before <code class="docutils literal notranslate"><span class="pre">coro_b()</span></code>’s output:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>I am coro_a(). Hi!
|
||||
I am coro_a(). Hi!
|
||||
I am coro_a(). Hi!
|
||||
I am coro_b(). I sure hope no one hogs the event loop...
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If we change <code class="docutils literal notranslate"><span class="pre">await</span> <span class="pre">coro_a()</span></code> to <code class="docutils literal notranslate"><span class="pre">await</span> <span class="pre">asyncio.create_task(coro_a())</span></code>, the
|
||||
behavior changes.
|
||||
The coroutine <code class="docutils literal notranslate"><span class="pre">main()</span></code> cedes control to the event loop with that statement.
|
||||
The event loop then proceeds through its backlog of work, calling <code class="docutils literal notranslate"><span class="pre">task_b</span></code>
|
||||
and then the task which wraps <code class="docutils literal notranslate"><span class="pre">coro_a()</span></code> before resuming the coroutine
|
||||
<code class="docutils literal notranslate"><span class="pre">main()</span></code>.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>I am coro_b(). I sure hope no one hogs the event loop...
|
||||
I am coro_a(). Hi!
|
||||
I am coro_a(). Hi!
|
||||
I am coro_a(). Hi!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This behavior of <code class="docutils literal notranslate"><span class="pre">await</span> <span class="pre">coroutine</span></code> can trip a lot of people up!
|
||||
That example highlights how using only <code class="docutils literal notranslate"><span class="pre">await</span> <span class="pre">coroutine</span></code> could
|
||||
unintentionally hog control from other tasks and effectively stall the event
|
||||
loop.
|
||||
<a class="reference internal" href="../library/asyncio-runner.html#asyncio.run" title="asyncio.run"><code class="xref py py-func docutils literal notranslate"><span class="pre">asyncio.run()</span></code></a> can help you detect such occurrences via the
|
||||
<code class="docutils literal notranslate"><span class="pre">debug=True</span></code> flag, which enables
|
||||
<a class="reference internal" href="../library/asyncio-dev.html#asyncio-debug-mode"><span class="std std-ref">debug mode</span></a>.
|
||||
Among other things, it will log any coroutines that monopolize execution for
|
||||
100ms or longer.</p>
|
||||
<p>The design intentionally trades off some conceptual clarity around usage of
|
||||
<code class="docutils literal notranslate"><span class="pre">await</span></code> for improved performance.
|
||||
Each time a task is awaited, control needs to be passed all the way up the
|
||||
call stack to the event loop.
|
||||
Then, the event loop needs to manage its internal state and work through
|
||||
its processing logic to resume the next job.
|
||||
That might sound minor, but in a large program with many <code class="docutils literal notranslate"><span class="pre">await</span></code>s, that
|
||||
overhead can add up to a non-negligible performance drag.</p>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
<section id="a-conceptual-overview-part-2-the-nuts-and-bolts">
|
||||
<h2>A conceptual overview part 2: the nuts and bolts<a class="headerlink" href="#a-conceptual-overview-part-2-the-nuts-and-bolts" title="Link to this heading">¶</a></h2>
|
||||
<p>Part 2 goes into detail on the mechanisms <code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code> uses to manage
|
||||
control flow.
|
||||
This is where the magic happens.
|
||||
You’ll come away from this section knowing what <code class="docutils literal notranslate"><span class="pre">await</span></code> does behind the scenes
|
||||
and how to make your own asynchronous operators.</p>
|
||||
<section id="the-inner-workings-of-coroutines">
|
||||
<h3>The inner workings of coroutines<a class="headerlink" href="#the-inner-workings-of-coroutines" title="Link to this heading">¶</a></h3>
|
||||
<p><code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code> leverages four components of Python to pass
|
||||
around control.</p>
|
||||
<p><a class="reference internal" href="../reference/expressions.html#generator.send" title="generator.send"><code class="xref py py-meth docutils literal notranslate"><span class="pre">coroutine.send(arg)</span></code></a> is the method used to start or
|
||||
resume a coroutine.
|
||||
If the coroutine was paused and is now being resumed, the argument <code class="docutils literal notranslate"><span class="pre">arg</span></code>
|
||||
will be sent in as the return value of the <code class="docutils literal notranslate"><span class="pre">yield</span></code> statement which originally
|
||||
paused it.
|
||||
If the coroutine is being used for the first time (as opposed to being resumed),
|
||||
<code class="docutils literal notranslate"><span class="pre">arg</span></code> must be <code class="docutils literal notranslate"><span class="pre">None</span></code>.</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="k">class</span><span class="w"> </span><span class="nc">Rock</span><span class="p">:</span>
|
||||
<span class="linenos"> 2</span> <span class="k">def</span><span class="w"> </span><span class="fm">__await__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="linenos"> 3</span> <span class="n">value_sent_in</span> <span class="o">=</span> <span class="k">yield</span> <span class="mi">7</span>
|
||||
<span class="linenos"> 4</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Rock.__await__ resuming with value: </span><span class="si">{</span><span class="n">value_sent_in</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
<span class="linenos"> 5</span> <span class="k">return</span> <span class="n">value_sent_in</span>
|
||||
<span class="linenos"> 6</span>
|
||||
<span class="linenos"> 7</span><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">main</span><span class="p">():</span>
|
||||
<span class="linenos"> 8</span> <span class="nb">print</span><span class="p">(</span><span class="s2">"Beginning coroutine main()."</span><span class="p">)</span>
|
||||
<span class="linenos"> 9</span> <span class="n">rock</span> <span class="o">=</span> <span class="n">Rock</span><span class="p">()</span>
|
||||
<span class="linenos">10</span> <span class="nb">print</span><span class="p">(</span><span class="s2">"Awaiting rock..."</span><span class="p">)</span>
|
||||
<span class="linenos">11</span> <span class="n">value_from_rock</span> <span class="o">=</span> <span class="k">await</span> <span class="n">rock</span>
|
||||
<span class="linenos">12</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Coroutine received value: </span><span class="si">{</span><span class="n">value_from_rock</span><span class="si">}</span><span class="s2"> from rock."</span><span class="p">)</span>
|
||||
<span class="linenos">13</span> <span class="k">return</span> <span class="mi">23</span>
|
||||
<span class="linenos">14</span>
|
||||
<span class="linenos">15</span><span class="n">coroutine</span> <span class="o">=</span> <span class="n">main</span><span class="p">()</span>
|
||||
<span class="linenos">16</span><span class="n">intermediate_result</span> <span class="o">=</span> <span class="n">coroutine</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
|
||||
<span class="linenos">17</span><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Coroutine paused and returned intermediate value: </span><span class="si">{</span><span class="n">intermediate_result</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
<span class="linenos">18</span>
|
||||
<span class="linenos">19</span><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Resuming coroutine and sending in value: 42."</span><span class="p">)</span>
|
||||
<span class="linenos">20</span><span class="k">try</span><span class="p">:</span>
|
||||
<span class="linenos">21</span> <span class="n">coroutine</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span>
|
||||
<span class="linenos">22</span><span class="k">except</span> <span class="ne">StopIteration</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||
<span class="linenos">23</span> <span class="n">returned_value</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
|
||||
<span class="linenos">24</span><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Coroutine main() finished and provided value: </span><span class="si">{</span><span class="n">returned_value</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><a class="reference internal" href="../reference/expressions.html#yieldexpr"><span class="std std-ref">yield</span></a>, as usual, pauses execution and returns control
|
||||
to the caller.
|
||||
In the example above, the <code class="docutils literal notranslate"><span class="pre">yield</span></code>, on line 3, is called by
|
||||
<code class="docutils literal notranslate"><span class="pre">...</span> <span class="pre">=</span> <span class="pre">await</span> <span class="pre">rock</span></code> on line 11.
|
||||
More broadly speaking, <code class="docutils literal notranslate"><span class="pre">await</span></code> calls the <a class="reference internal" href="../reference/datamodel.html#object.__await__" title="object.__await__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__await__()</span></code></a> method of
|
||||
the given object.
|
||||
<code class="docutils literal notranslate"><span class="pre">await</span></code> also does one more very special thing: it propagates (or “passes
|
||||
along”) any <code class="docutils literal notranslate"><span class="pre">yield</span></code>s it receives up the call chain.
|
||||
In this case, that’s back to <code class="docutils literal notranslate"><span class="pre">...</span> <span class="pre">=</span> <span class="pre">coroutine.send(None)</span></code> on line 16.</p>
|
||||
<p>The coroutine is resumed via the <code class="docutils literal notranslate"><span class="pre">coroutine.send(42)</span></code> call on line 21.
|
||||
The coroutine picks back up from where it <code class="docutils literal notranslate"><span class="pre">yield</span></code>ed (or paused) on line 3
|
||||
and executes the remaining statements in its body.
|
||||
When a coroutine finishes, it raises a <a class="reference internal" href="../builtins/exceptions.html#StopIteration" title="StopIteration"><code class="xref py py-exc docutils literal notranslate"><span class="pre">StopIteration</span></code></a> exception with the
|
||||
return value attached in the <a class="reference internal" href="../builtins/exceptions.html#StopIteration.value" title="StopIteration.value"><code class="xref py py-attr docutils literal notranslate"><span class="pre">value</span></code></a> attribute.</p>
|
||||
<p>That snippet produces this output:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Beginning coroutine main().
|
||||
Awaiting rock...
|
||||
Coroutine paused and returned intermediate value: 7.
|
||||
Resuming coroutine and sending in value: 42.
|
||||
Rock.__await__ resuming with value: 42.
|
||||
Coroutine received value: 42 from rock.
|
||||
Coroutine main() finished and provided value: 23.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>It’s worth pausing for a moment here and making sure you followed the various
|
||||
ways that control flow and values were passed. A lot of important ideas were
|
||||
covered and it’s worth ensuring your understanding is firm.</p>
|
||||
<p>The only way to yield (or effectively cede control) from a coroutine is to
|
||||
<code class="docutils literal notranslate"><span class="pre">await</span></code> an object that <code class="docutils literal notranslate"><span class="pre">yield</span></code>s in its <code class="docutils literal notranslate"><span class="pre">__await__</span></code> method.
|
||||
That might sound odd to you. You might be thinking:</p>
|
||||
<blockquote>
|
||||
<div><p>1. What about a <code class="docutils literal notranslate"><span class="pre">yield</span></code> directly within the coroutine function? The
|
||||
coroutine function becomes an
|
||||
<a class="reference internal" href="../reference/expressions.html#asynchronous-generator-functions"><span class="std std-ref">async generator function</span></a>, a
|
||||
different beast entirely.</p>
|
||||
<p>2. What about a <a class="reference internal" href="../reference/expressions.html#yieldexpr"><span class="std std-ref">yield from</span></a> within the coroutine function to a (plain)
|
||||
generator?
|
||||
That causes the error: <code class="docutils literal notranslate"><span class="pre">SyntaxError:</span> <span class="pre">yield</span> <span class="pre">from</span> <span class="pre">not</span> <span class="pre">allowed</span> <span class="pre">in</span> <span class="pre">a</span> <span class="pre">coroutine.</span></code>
|
||||
This was intentionally designed for the sake of simplicity – mandating only
|
||||
one way of using coroutines.
|
||||
Despite that, <code class="docutils literal notranslate"><span class="pre">yield</span> <span class="pre">from</span></code> and <code class="docutils literal notranslate"><span class="pre">await</span></code> effectively do the same thing.
|
||||
Initially <code class="docutils literal notranslate"><span class="pre">yield</span></code> was barred as well, but was re-accepted to allow for
|
||||
async generators.</p>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
<section id="futures">
|
||||
<h3>Futures<a class="headerlink" href="#futures" title="Link to this heading">¶</a></h3>
|
||||
<p>A <a class="reference internal" href="../library/asyncio-future.html#asyncio-future-obj"><span class="std std-ref">future</span></a> is an object meant to represent a
|
||||
computation’s status and result.
|
||||
The term is a nod to the idea of something still to come or not yet happened,
|
||||
and the object is a way to keep an eye on that something.</p>
|
||||
<p>A future has a few important attributes. One is its state, which can be either
|
||||
“pending”, “cancelled”, or “done”.
|
||||
Another is its result, which is set when the state transitions to done.
|
||||
Unlike a coroutine, a future does not represent the actual computation to be
|
||||
done; instead, it represents the status and result of that computation, kind of
|
||||
like a status light (red, yellow, or green) or indicator.</p>
|
||||
<p><a class="reference internal" href="../library/asyncio-task.html#asyncio.Task" title="asyncio.Task"><code class="xref py py-class docutils literal notranslate"><span class="pre">asyncio.Task</span></code></a> subclasses <a class="reference internal" href="../library/asyncio-future.html#asyncio.Future" title="asyncio.Future"><code class="xref py py-class docutils literal notranslate"><span class="pre">asyncio.Future</span></code></a> in order to gain
|
||||
these various capabilities.
|
||||
The prior section said tasks store a list of callbacks, which wasn’t entirely
|
||||
accurate.
|
||||
It’s actually the <code class="docutils literal notranslate"><span class="pre">Future</span></code> class that implements this logic, which <code class="docutils literal notranslate"><span class="pre">Task</span></code>
|
||||
inherits.</p>
|
||||
<p>Futures may also be used directly (not via tasks).
|
||||
Tasks mark themselves as done when their coroutine is complete.
|
||||
Futures are much more versatile and will be marked as done when you say so.
|
||||
In this way, they’re the flexible interface for you to make your own conditions
|
||||
for waiting and resuming.</p>
|
||||
</section>
|
||||
<section id="a-homemade-asyncio-sleep">
|
||||
<h3>A homemade asyncio.sleep<a class="headerlink" href="#a-homemade-asyncio-sleep" title="Link to this heading">¶</a></h3>
|
||||
<p>We’ll go through an example of how you could leverage a future to create your
|
||||
own variant of asynchronous sleep (<code class="docutils literal notranslate"><span class="pre">async_sleep</span></code>) which mimics
|
||||
<a class="reference internal" href="../library/asyncio-task.html#asyncio.sleep" title="asyncio.sleep"><code class="xref py py-func docutils literal notranslate"><span class="pre">asyncio.sleep()</span></code></a>.</p>
|
||||
<p>This snippet registers a few tasks with the event loop and then awaits the task
|
||||
created by <code class="docutils literal notranslate"><span class="pre">asyncio.create_task</span></code>, which wraps the <code class="docutils literal notranslate"><span class="pre">async_sleep(3)</span></code> coroutine.
|
||||
We want that task to finish only after three seconds have elapsed, but without
|
||||
preventing other tasks from running.</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">other_work</span><span class="p">():</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="s2">"I like work. Work work."</span><span class="p">)</span>
|
||||
|
||||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">main</span><span class="p">():</span>
|
||||
<span class="c1"># Add a few other tasks to the event loop, so there's something</span>
|
||||
<span class="c1"># to do while asynchronously sleeping.</span>
|
||||
<span class="n">work_tasks</span> <span class="o">=</span> <span class="p">[</span>
|
||||
<span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">other_work</span><span class="p">()),</span>
|
||||
<span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">other_work</span><span class="p">()),</span>
|
||||
<span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">other_work</span><span class="p">())</span>
|
||||
<span class="p">]</span>
|
||||
<span class="nb">print</span><span class="p">(</span>
|
||||
<span class="s2">"Beginning asynchronous sleep at time: "</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%H:%M:%S"</span><span class="p">)</span><span class="si">}</span><span class="s2">."</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">async_sleep</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>
|
||||
<span class="nb">print</span><span class="p">(</span>
|
||||
<span class="s2">"Done asynchronous sleep at time: "</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%H:%M:%S"</span><span class="p">)</span><span class="si">}</span><span class="s2">."</span>
|
||||
<span class="p">)</span>
|
||||
<span class="c1"># asyncio.gather effectively awaits each task in the collection.</span>
|
||||
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">gather</span><span class="p">(</span><span class="o">*</span><span class="n">work_tasks</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Below, we use a future to enable custom control over when that task will be
|
||||
marked as done.
|
||||
If <a class="reference internal" href="../library/asyncio-future.html#asyncio.Future.set_result" title="asyncio.Future.set_result"><code class="xref py py-meth docutils literal notranslate"><span class="pre">future.set_result()</span></code></a> (the method
|
||||
responsible for marking that future as done) is never called, then this task
|
||||
will never finish.
|
||||
We’ve also enlisted the help of another task, which we’ll see in a moment, that
|
||||
will monitor how much time has elapsed and, accordingly, call
|
||||
<code class="docutils literal notranslate"><span class="pre">future.set_result()</span></code>.</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">async_sleep</span><span class="p">(</span><span class="n">seconds</span><span class="p">:</span> <span class="nb">float</span><span class="p">):</span>
|
||||
<span class="n">future</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">Future</span><span class="p">()</span>
|
||||
<span class="n">time_to_wake</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">+</span> <span class="n">seconds</span>
|
||||
<span class="c1"># Add the watcher-task to the event loop.</span>
|
||||
<span class="n">watcher_task</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">_sleep_watcher</span><span class="p">(</span><span class="n">future</span><span class="p">,</span> <span class="n">time_to_wake</span><span class="p">))</span>
|
||||
<span class="c1"># Block until the future is marked as done.</span>
|
||||
<span class="k">await</span> <span class="n">future</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Below, we use a rather bare <code class="docutils literal notranslate"><span class="pre">YieldToEventLoop()</span></code> object to <code class="docutils literal notranslate"><span class="pre">yield</span></code>
|
||||
from its <code class="docutils literal notranslate"><span class="pre">__await__</span></code> method, ceding control to the event loop.
|
||||
This is effectively the same as calling <code class="docutils literal notranslate"><span class="pre">asyncio.sleep(0)</span></code>, but this approach
|
||||
offers more clarity, not to mention it’s somewhat cheating to use
|
||||
<code class="docutils literal notranslate"><span class="pre">asyncio.sleep</span></code> when showcasing how to implement it!</p>
|
||||
<p>As usual, the event loop cycles through its tasks, giving them control
|
||||
and receiving control back when they pause or finish.
|
||||
The <code class="docutils literal notranslate"><span class="pre">watcher_task</span></code>, which runs the coroutine <code class="docutils literal notranslate"><span class="pre">_sleep_watcher(...)</span></code>, will
|
||||
be invoked once per full cycle of the event loop.
|
||||
On each resumption, it’ll check the time and if not enough has elapsed, then
|
||||
it’ll pause once again and hand control back to the event loop.
|
||||
Once enough time has elapsed, <code class="docutils literal notranslate"><span class="pre">_sleep_watcher(...)</span></code>
|
||||
marks the future as done and completes by exiting its
|
||||
infinite <code class="docutils literal notranslate"><span class="pre">while</span></code> loop.
|
||||
Given this helper task is only invoked once per cycle of the event loop,
|
||||
you’d be correct to note that this asynchronous sleep will sleep <em>at least</em>
|
||||
three seconds, rather than exactly three seconds.
|
||||
Note this is also true of <code class="docutils literal notranslate"><span class="pre">asyncio.sleep</span></code>.</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">YieldToEventLoop</span><span class="p">:</span>
|
||||
<span class="k">def</span><span class="w"> </span><span class="fm">__await__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">yield</span>
|
||||
|
||||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">_sleep_watcher</span><span class="p">(</span><span class="n">future</span><span class="p">,</span> <span class="n">time_to_wake</span><span class="p">):</span>
|
||||
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">>=</span> <span class="n">time_to_wake</span><span class="p">:</span>
|
||||
<span class="c1"># This marks the future as done.</span>
|
||||
<span class="n">future</span><span class="o">.</span><span class="n">set_result</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
|
||||
<span class="k">break</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="k">await</span> <span class="n">YieldToEventLoop</span><span class="p">()</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here is the full program’s output:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>$ python custom-async-sleep.py
|
||||
Beginning asynchronous sleep at time: 14:52:22.
|
||||
I like work. Work work.
|
||||
I like work. Work work.
|
||||
I like work. Work work.
|
||||
Done asynchronous sleep at time: 14:52:25.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You might feel this implementation of asynchronous sleep was unnecessarily
|
||||
convoluted.
|
||||
And, well, it was.
|
||||
The example was meant to showcase the versatility of futures with a simple
|
||||
example that could be mimicked for more complex needs.
|
||||
For reference, you could implement it without futures, like so:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">simpler_async_sleep</span><span class="p">(</span><span class="n">seconds</span><span class="p">):</span>
|
||||
<span class="n">time_to_wake</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">+</span> <span class="n">seconds</span>
|
||||
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">>=</span> <span class="n">time_to_wake</span><span class="p">:</span>
|
||||
<span class="k">return</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="k">await</span> <span class="n">YieldToEventLoop</span><span class="p">()</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>But that’s all for now. Hopefully you’re ready to more confidently dive into
|
||||
some async programming or check out advanced topics in the
|
||||
<a class="reference internal" href="../library/asyncio.html#module-asyncio" title="asyncio: Asynchronous I/O."><code class="xref py py-mod docutils literal notranslate"><span class="pre">rest</span> <span class="pre">of</span> <span class="pre">the</span> <span class="pre">documentation</span></code></a>.</p>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="Main">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<div>
|
||||
<h3><a href="../contents.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">A conceptual overview of <code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code></a><ul>
|
||||
<li><a class="reference internal" href="#a-conceptual-overview-part-1-the-high-level">A conceptual overview part 1: the high-level</a><ul>
|
||||
<li><a class="reference internal" href="#event-loop">Event loop</a></li>
|
||||
<li><a class="reference internal" href="#asynchronous-functions-and-coroutines">Asynchronous functions and coroutines</a></li>
|
||||
<li><a class="reference internal" href="#tasks">Tasks</a></li>
|
||||
<li><a class="reference internal" href="#await">await</a><ul>
|
||||
<li><a class="reference internal" href="#awaiting-tasks">Awaiting tasks</a></li>
|
||||
<li><a class="reference internal" href="#awaiting-coroutines">Awaiting coroutines</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#a-conceptual-overview-part-2-the-nuts-and-bolts">A conceptual overview part 2: the nuts and bolts</a><ul>
|
||||
<li><a class="reference internal" href="#the-inner-workings-of-coroutines">The inner workings of coroutines</a></li>
|
||||
<li><a class="reference internal" href="#futures">Futures</a></li>
|
||||
<li><a class="reference internal" href="#a-homemade-asyncio-sleep">A homemade asyncio.sleep</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="index.html"
|
||||
title="previous chapter">Python HOWTOs</a></p>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="cporting.html"
|
||||
title="next chapter">Porting Extension Modules to Python 3</a></p>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const title = document.querySelector('meta[property="og:title"]').content;
|
||||
const elements = document.querySelectorAll('.improvepage');
|
||||
const pageurl = window.location.href.split('?')[0];
|
||||
elements.forEach(element => {
|
||||
const url = new URL(element.href.split('?')[0].replace("-nojs", ""));
|
||||
url.searchParams.set('pagetitle', title);
|
||||
url.searchParams.set('pageurl', pageurl);
|
||||
url.searchParams.set('pagesource', "howto/a-conceptual-overview-of-asyncio.rst");
|
||||
element.href = url.toString();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div role="note" aria-label="source link">
|
||||
<h3>This page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../bugs.html">Report a bug</a></li>
|
||||
<li><a class="improvepage" href="../improve-page-nojs.html">Improve this page</a></li>
|
||||
<li>
|
||||
<a href="https://github.com/python/cpython/blob/3.15/Doc/howto/a-conceptual-overview-of-asyncio.rst?plain=1"
|
||||
rel="nofollow">Show source
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="sidebarbutton" title="Collapse sidebar">
|
||||
<span>«</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="Related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="cporting.html" title="Porting Extension Modules to Python 3"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="index.html" title="Python HOWTOs"
|
||||
>previous</a> |</li>
|
||||
|
||||
<li class="root-icon"><img src="../_static/py.svg" alt="Python logo" style="vertical-align: middle; margin-top: -1px"></li>
|
||||
<li><a href="https://www.python.org/">Python</a> »</li>
|
||||
<li class="switchers">
|
||||
<div class="language_switcher_placeholder"></div>
|
||||
<div class="version_switcher_placeholder"></div>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
</li>
|
||||
<li id="cpython-language-and-version">
|
||||
<a href="../index.html">3.15.0 Documentation</a> »
|
||||
</li>
|
||||
|
||||
<li class="nav-item nav-item-1"><a href="index.html" >Python HOWTOs</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">A conceptual overview of <code class="xref py py-mod docutils literal notranslate"><span class="pre">asyncio</span></code></a></li>
|
||||
<li class="right">
|
||||
|
||||
|
||||
<div class="inline-search" role="search">
|
||||
<form class="inline-search" action="../search.html" method="get">
|
||||
<input placeholder="Quick search" aria-label="Quick search" type="search" name="q" id="search-box">
|
||||
<input type="submit" value="Go">
|
||||
</form>
|
||||
</div>
|
||||
|
|
||||
</li>
|
||||
<li class="right">
|
||||
<label class="theme-selector-label">
|
||||
Theme
|
||||
<select class="theme-selector" oninput="activateTheme(this.value)">
|
||||
<option value="auto" selected>Auto</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="dark">Dark</option>
|
||||
</select>
|
||||
</label> |</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© <a href="../copyright.html">Copyright</a> 2001 Python Software Foundation.
|
||||
<br>
|
||||
This page is licensed under the Python Software Foundation License Version 2.
|
||||
<br>
|
||||
Examples, recipes, and other code in the documentation are additionally licensed under the Zero Clause BSD License.
|
||||
<br>
|
||||
|
||||
See <a href="/license.html">History and License</a> for more information.<br>
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
The Python Software Foundation is a non-profit corporation.
|
||||
<a href="https://www.python.org/psf/donations/">Please donate.</a>
|
||||
<br>
|
||||
<br>
|
||||
Last updated on Oct 09, 2026 (11:35 UTC).
|
||||
|
||||
<a href="/bugs.html">Found a bug</a>?
|
||||
|
||||
<br>
|
||||
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.2.3.
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user