_NO_RESULT = object()
-def chain_future(a: "Future[_T]", b: "Future[_T]") -> None:
+def chain_future(
+ a: Union["Future[_T]", "futures.Future[_T]"],
+ b: Union["Future[_T]", "futures.Future[_T]"],
+) -> None:
"""Chain two futures together so that when one completes, so does the other.
The result (success or failure) of ``a`` will be copied to ``b``, unless
from tornado.concurrent import (
Future,
+ chain_future,
run_on_executor,
future_set_result_unless_cancelled,
)
self.assertEqual(fut.result(), 42)
+class ChainFutureTest(AsyncTestCase):
+ @gen_test
+ async def test_asyncio_futures(self):
+ fut: Future[int] = Future()
+ fut2: Future[int] = Future()
+ chain_future(fut, fut2)
+ fut.set_result(42)
+ result = await fut2
+ self.assertEqual(result, 42)
+
+ @gen_test
+ async def test_concurrent_futures(self):
+ # A three-step chain: two concurrent futures (showing that both arguments to chain_future
+ # can be concurrent futures), and then one from a concurrent future to an asyncio future so
+ # we can use it in await.
+ fut: futures.Future[int] = futures.Future()
+ fut2: futures.Future[int] = futures.Future()
+ fut3: Future[int] = Future()
+ chain_future(fut, fut2)
+ chain_future(fut2, fut3)
+ fut.set_result(42)
+ result = await fut3
+ self.assertEqual(result, 42)
+
+
# The following series of classes demonstrate and test various styles
# of use, with and without generators and futures.