Skip to content

Commit

Permalink
Some improvements to notebook 05
Browse files Browse the repository at this point in the history
  • Loading branch information
jmoldow committed Oct 24, 2017
1 parent c22b22a commit 0b8e95e
Showing 1 changed file with 37 additions and 37 deletions.
74 changes: 37 additions & 37 deletions 05-event-loop-futures.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,14 @@
"cells": [
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"metadata": {},
"source": [
"# Futures"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"metadata": {},
"source": [
"A future is some object that represents an asynchronous computation. Like how a synchronous function returns a result, an asynchronous function may return a future. Rather than the function blocking when called, it can return the future immediately.\n",
"\n",
Expand Down Expand Up @@ -53,20 +47,14 @@
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"metadata": {},
"source": [
"# Event Loop Futures"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"metadata": {},
"source": [
"Event loop futures are very similar to threading futures. The main difference is that the asynchronous computation is happening on the same thread, in the same event loop.\n",
"\n",
Expand All @@ -77,20 +65,14 @@
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"metadata": {},
"source": [
"# `asyncio.Future`"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"metadata": {},
"source": [
"So far we've been discussing event loops in general terms, and deriving them from first principles. At this point, it makes sense to start focusing on the particulars of the `asyncio` interface. This certainly isn't the only way to do event loop futures, but it is the way that was chosen for the standard library implementation.\n",
"\n",
Expand All @@ -108,20 +90,24 @@
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Starting good task\n",
"<Future done=False>\n",
"Add done callback <TaskWakeupMethWrapper object at 0x10ba2c498> to <Future done=False cb=[<TaskWakeupMethWrapper object at 0x10ba2c498>()]>\n",
"Set result 10 for <Future done=True result=10 cb=[<TaskWakeupMethWrapper object at 0x10ba2c498>()]>\n",
"Scheduling callback <TaskWakeupMethWrapper object at 0x10ba2c498> for <Future done=True result=10 cb=[<TaskWakeupMethWrapper object at 0x10ba2c498>()]>\n",
"Task resumed from waiting on <Future done=True result=10>\n",
"Waiting on future\n",
"Add done callback <TaskWakeupMethWrapper object at 0x10a8b93a8> to <Future done=False cb=[<TaskWakeupMethWrapper object at 0x10a8b93a8>()]>\n",
"Set result 10 for <Future done=True result=10 cb=[<TaskWakeupMethWrapper object at 0x10a8b93a8>()]>\n",
"Scheduling callback <TaskWakeupMethWrapper object at 0x10a8b93a8> for <Future done=True result=10 cb=[<TaskWakeupMethWrapper object at 0x10a8b93a8>()]>\n",
"Task resumed from waiting on <generator object Future.__iter__ at 0x10a8aea40>\n",
"Result: 10\n",
"Didn't ever yield\n",
"Result: None\n",
"Yielding\n",
"Resumed\n",
"\n"
]
},
Expand All @@ -130,9 +116,9 @@
"output_type": "stream",
"text": [
"Traceback (most recent call last):\n",
" File \"<ipython-input-1-2c0e844f2988>\", line 105, in bad_task\n",
" File \"<ipython-input-1-2819b86a00b7>\", line 114, in bad_task\n",
" result = (yield future)\n",
"RuntimeError: yield was used instead of yield from in task <Task pending coro=<bad_task() running at <ipython-input-1-2c0e844f2988>:105>> with <Future done=False>\n"
"RuntimeError: yield was used instead of yield from in task <Task pending coro=<bad_task() running at <ipython-input-1-2819b86a00b7>:114>> with <Future done=False>\n"
]
}
],
Expand Down Expand Up @@ -227,12 +213,21 @@
"loop = asyncio.new_event_loop()\n",
" \n",
"def good_task(*, loop):\n",
" print('Starting good task')\n",
" future = Future(loop=loop)\n",
" print(future)\n",
" loop.call_soon(future.set_result, 10)\n",
" print('Waiting on future')\n",
" future = iter(future)\n",
" result = (yield from future)\n",
" print(\"Task resumed from waiting on\", future)\n",
" print(\"Result:\", result)\n",
" result2 = (yield from future)\n",
" print(\"Didn't ever yield\")\n",
" print(\"Result:\", result2)\n",
" print(\"Yielding\")\n",
" yield\n",
" print(\"Resumed\")\n",
" \n",
"asyncio.ensure_future(good_task(loop=loop), loop=loop)\n",
"for _ in range(100):\n",
Expand All @@ -252,13 +247,18 @@
]
},
{
"cell_type": "code",
"execution_count": null,
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
"source": [
"This snippet shows the basic mechanics of `asyncio.Future`. For simplicity, I did leave out some other functionality it has:\n",
"- A future can also be completed with the `set_exception()` method, which will cause that exception to be raised inside the task when `result()` is called.\n",
"- A future can be cancelled. This causes any tasks waiting on the future to also be cancelled.\n",
"- Callbacks can be un-registered, if the event loop or another task determines that the callback is no longer needed.\n",
"\n",
"Notice that `asyncio.Future` is not protected by any threading locks. This is one of the advantages of using an event loop instead of using threads. Since everything is running on the same thread, you don't get any surprise context switches. Every evaluation step is guaranteed to execute immediately after the previous evaulation step, unless the previous step is a `yield` or `yield from`. This drastically reduces the number of of ways in which your program to execute, and makes it much easier to reason about."
]
}
],
"metadata": {
Expand Down

0 comments on commit 0b8e95e

Please sign in to comment.