<div dir="ltr"><div><div>As I wrote on the issue, I'm -1 on this proposal. Not only does this API encourage beginners to ignore the essential difference between synchronous functions meant to run in a thread (using synchronous I/O and pre-emptive CPU scheduling) and asyncio coroutines/tasks (which use overlapped I/O and require explicit scheduling), it also encourages avoiding the "await" primitive (formerly "yield from") in favor of a function call which cannot be used from within a coroutine/task.<br><br></div>This particular spelling moreover introduces a "similarity" between foreground and background tasks that doesn't actually exist.<br><br></div>The example suggests that this should really be a pair of convenience functions in collections.futures, as it does not make any use of asyncio.<br></div><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Jul 10, 2015 at 12:49 PM, Nick Coghlan <span dir="ltr"><<a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi folks,<br>
<br>
Based on the recent discussions Sven kicked off regarding the<br>
complexity of interacting with asyncio from otherwise synchronous<br>
code, I came up with an API design that I like inspired by the way<br>
background and foreground tasks in the POSIX shell work.<br>
<br>
My blog post about this design is at<br>
<a href="http://www.curiousefficiency.org/posts/2015/07/asyncio-background-calls.html" rel="noreferrer" target="_blank">http://www.curiousefficiency.org/posts/2015/07/asyncio-background-calls.html</a>,<br>
but the essential components are the following two APIs:<br>
<br>
    def run_in_background(target, *, loop=None):<br>
        """Schedules target as a background task<br>
<br>
        Returns the scheduled task.<br>
<br>
        If target is a future or coroutine, equivalent to asyncio.ensure_future<br>
        If target is a callable, it is scheduled in the default executor<br>
        """<br>
        ...<br>
<br>
    def run_in_foreground(task, *, loop=None):<br>
        """Runs event loop in current thread until the given task completes<br>
<br>
        Returns the result of the task.<br>
        For more complex conditions, combine with asyncio.wait()<br>
        To include a timeout, combine with asyncio.wait_for()<br>
        """<br>
        ...<br>
<br>
run_in_background is akin to invoking a shell command with a trailing<br>
"&" - it puts the operation into the background, leaving the current<br>
thread to move on to the next operation (or wait for input at the<br>
REPL). When coroutines are scheduled, they won't start running until<br>
you start a foreground task, while callables delegated to the default<br>
executor will start running immediately.<br>
<br>
To actually get the *results* of that task, you have to run it in the<br>
foreground of the current thread using run_in_foreground - this is<br>
akin to bringing a background process to the foreground of a shell<br>
session using "fg".<br>
<br>
To relate this idea back to some of the examples Sven was discussing,<br>
here's how translating some old serialised synchronous code to use<br>
those APIs might look in practice:<br>
<br>
    # Serial synchronous data loading<br>
    def load_and_process_data():<br>
        data1 = load_remote_data_set1()<br>
        data2 = load_remote_data_set2()<br>
        return process_data(data1, data2)<br>
<br>
    # Parallel asynchronous data loading<br>
    def load_and_process_data():<br>
        future1 = asyncio.run_in_background(load_remote_data_set1_async())<br>
        future2 = asyncio.run_in_background(load_remote_data_set2_async())<br>
        data1 = asyncio.run_in_foreground(future1)<br>
        data2 = asyncio.run_in_foreground(future2)<br>
        return process_data(data1, data2)<br>
<br>
The application remains fundamentally synchronous, but the asyncio<br>
event loop is exploited to obtain some local concurrency in waiting<br>
for client IO operations.<br>
<br>
Regards,<br>
Nick.<br>
<br>
P.S. time.sleep() and asyncio.sleep() are rather handy as standins for<br>
blocking and non-blocking IO operations. I wish I'd remembered that<br>
earlier :)<br>
<span class="HOEnZb"><font color="#888888"><br>
--<br>
Nick Coghlan   |   <a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a>   |   Brisbane, Australia<br>
_______________________________________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org">Python-ideas@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-ideas</a><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer" target="_blank">http://python.org/psf/codeofconduct/</a><br>
</font></span></blockquote></div><br><br clear="all"><br>-- <br><div class="gmail_signature">--Guido van Rossum (<a href="http://python.org/~guido" target="_blank">python.org/~guido</a>)</div>
</div>