
{"id":4871,"date":"2023-08-04T14:06:03","date_gmt":"2023-08-04T18:06:03","guid":{"rendered":"https:\/\/ikriv.com\/blog\/?p=4871"},"modified":"2024-10-04T14:02:36","modified_gmt":"2024-10-04T18:02:36","slug":"python-why-do-we-need-async-for","status":"publish","type":"post","link":"https:\/\/ikriv.com\/blog\/?p=4871","title":{"rendered":"Python: why do we need async for?"},"content":{"rendered":"<p>In Python,\u00a0 <code>async for<\/code> uses asynchronous generator to iterate over values. But how is it different from a regular <code>for<\/code> that uses a generator that yields awaitables?<br \/>\n<strong>TL;DR<\/strong>: not much, except some peculiarities when ending the loop. The answer is in <a href=\"https:\/\/stackoverflow.com\/questions\/65547322\/difference-between-asynciterablet-and-iterableawaitablet\">this StackOverflow post<\/a>, but it lacks examples. I give concrete examples in my blog post.<\/p>\n<p>Suppose we iterate over values from an async generator:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nasync def asyncGetValues():\r\n    await ...something...\r\n    yield value\r\n\r\nasync for x in asyncGetNextValues():\r\n    print (value)\r\n<\/pre>\n<p>How is it different from:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ndef getAwaitables():\r\n    async def asyncGetValue():\r\n        await ...something...;\r\n        return value\r\n    yield asyncGetValue()\r\n\r\nfor get_x in getAwaitables():\r\n    print(await get_x)\r\n<\/pre>\n<p>Besides having to write a little bit more code, the difference is in stopping the loop. Suppose we want to iterate over numbers that come from a remote source, and want to stop when we received a zero. We need to wait for each number to come, and we don&#8217;t know in advance whether the result would be zero. I will emulate it using <code>sleep()<\/code> and random numbers:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nasync def asyncGetNumberStream():\r\n    while True:\r\n        await asyncio.sleep(1)\r\n    n = random.randint(0, 4)\r\n    if n==0:\r\n        return\r\n    yield n\r\n<\/pre>\n<p>We can use it as follows:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nasync for n in asyncGetNumberStream():\r\n    print(f&quot;Received {n}&quot;)\r\n<\/pre>\n<p>The loop will exit when we receive a zero, but zero will never be returned to the user and printed.<\/p>\n<p>Let&#8217;s rewrite it using a regular generator:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n# generates a stream of Awaitable&#x5B;int]\r\ndef getNumberStream():\r\n    async def getNextNumber():\r\n        await asyncio.sleep(1)\r\n        return random.randint(0, 4)\r\n    while True:\r\n        yield getNextNumber()\r\n<\/pre>\n<p>Do you see the problem? It&#8217;s an infinite generator now, because we don&#8217;t know when to stop. We must return the next awaitable before we know whether it will return a zero or some other number. It&#8217;s up to the loop&#8217;s user now to stop at zero:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nfor get_n in getNumberStream():\r\n    n = await get_n\r\n    if n==0:\r\n        break\r\n    print(f&quot;Received {n}&quot;)\r\n<\/pre>\n<p>This illustrates the main difference between regular for over awaitables and async for. If we know the number of items in advance, or if the sequence is infinite, there is not much difference. Regular for is a little bulkier, but functionally it&#8217;s the same. But if we need to stop on some dynamically calculated condition, with async for we can embed this condition into the generator, while with regular for the loop will be technically &#8216;infinite&#8217;, and it is the user who will have to determine when to stop.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Python,\u00a0 async for uses asynchronous generator to iterate over values. But how is it different from a regular for that uses a generator that yields awaitables? TL;DR: not much, <a href=\"https:\/\/ikriv.com\/blog\/?p=4871\" class=\"more-link\">[&hellip;]<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"Layout":"","footnotes":""},"categories":[26],"tags":[],"class_list":["entry","author-ikriv","post-4871","post","type-post","status-publish","format-standard","category-python"],"_links":{"self":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4871","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4871"}],"version-history":[{"count":5,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4871\/revisions"}],"predecessor-version":[{"id":4876,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4871\/revisions\/4876"}],"wp:attachment":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4871"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4871"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4871"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}