Convert callback-style asynchronous API to async / await in Python

Overview

Starting with Python 3.5, you can use async / await for asynchronous programming. However, many existing libraries have a traditional callback-type asynchronous API, and async / await cannot be applied as it is. Therefore, we will convert such a callback type API into a form that can be used with async / await.

Callback Asynchronous API Example

Add arguments a and b on another thread and return the result in a callback Consider the async_add method below.

import time
import threading

def async_add(a, b, callback):
    def run():
        time.sleep(1)
        callback(a + b)
    thread = threading.Thread(target=run)
    thread.start()

Let's use this to do a 1 + 2 + 3 calculation. It will be as follows.

async_add(1, 2, lambda result1: \
        async_add(result1, 3, lambda result2: \
                print(result2)))

Callbacks are nested and complicated. I would like to wrap this so that I can use await to write an image like this:

result1 = await awaitable_async_add(1, 2)
result2 = await awaitable_async_add(result1, 3)
print(result2)

Implementation

code

import time
import threading
import asyncio

def async_add(a, b, callback):
    def run():
        time.sleep(1)
        callback(a + b)
    thread = threading.Thread(target=run)
    thread.start()

def awaitable_async_add(a, b, loop):
    f = asyncio.Future() # (1)
    def callback(result):
        loop.call_soon_threadsafe(
                lambda: f.set_result(result)) #(2)
    async_add(a, b, callback) # (1)
    return f # (1)

async def exec(loop):
    result1 = await awaitable_async_add(1, 2, loop)
    result2 = await awaitable_async_add(result1, 3, loop)
    print(result2)

loop = asyncio.get_event_loop() # (3)
loop.run_until_complete(exec(loop)) # (3)
loop.stop()

Execution result

6

Commentary

(1) When the awaitable_async_add method is called, it starts executing the async_add method and immediately returns an asyncio.Future object. At this point, the calculation process is not complete yet.

(2) The callback is called when the calculation process is completed. By calling the set_result () method of the asyncio.Future object with the processing result as an argument, the asyncio.Future object is notified of the completion of processing and the calculation result is returned.

(3) Acquire the event loop and execute the process until the asynchronous process is completed.

One thing to note here is loop.call_soon_threadsafe in (2). If you try to call f.set_result directly as shown below, an error will occur.

    def callback(result):
        f.set_result(result)

Execution result

RuntimeError: Non-thread-safe operation invoked on an event loop other than the current one

This error is due to the set_result method being called on a different thread than the event loop running in (3). Since asyncio.Future is not thread-safe, the set_result method must always be called in the same thread as the event loop. Therefore, by passing the process as a callback to the call_soon_threadsafe method of the event loop, set_result is called in the event loop.

reference

https://docs.python.org/3.6/library/asyncio.html

Recommended Posts

Convert callback-style asynchronous API to async / await in Python
Convert markdown to PDF in Python
Hide websockets async / await in Python3
[Python] Asynchronous request with async / await
Convert psd file to png in Python
Convert from Markdown to HTML in Python
Convert absolute URLs to relative URLs in Python
Convert PDFs to images in bulk with Python
Convert exponential notation float to str in Python
How to write async and await in Vue.js
Convert cubic mesh code to WKT in Python
python async / await curio
C API in Python 3
Convert timezoned date and time to Unixtime in Python2.7
How to convert / restore a string with [] in python
Convert NumPy array "ndarray" to lilt in Python [tolist ()]
Convert CIDR notation netmask to dotted decimal notation in Python
How to convert floating point numbers to binary numbers in Python
Convert the image in .zip to PDF with Python
Convert / return class object to JSON format in Python
Python asynchronous processing ~ Full understanding of async and await ~
[Python] Created a method to convert radix in 1 second
Convert Webpay Entity type to Dict type (recursively in Python)
Hit Mastodon's API in Python
Scraping using Python 3.5 async / await
To flush stdout in Python
Convert numpy int64 to python int
[Python] Convert list to Pandas [Pandas]
Login to website in Python
Asynchronous processing (threading) in python
Convert Scratch project to Python
[Python] Convert Shift_JIS to UTF-8
Convert CIDR notation in Python
Speech to speech in python [text to speech]
Blender Python API in Houdini (Python 3)
How to develop in Python
Convert python 3.x code to python 2.x
Post to Slack in Python
Lightweight thread performance benchmark using async / await implemented in Python 3.5
I tried to create API list.csv in Python from swagger.yaml
Convert images passed to Jason Statham-like in Python to ASCII art
Convert Excel file to text in Python for diff purposes
Call github api in python to get pull request information
An easy way to hit the Amazon Product API in Python
Determine the date and time format in Python and convert to Unixtime
[Python] How to do PCA in Python
Getting the arXiv API in Python
How to collect images in Python
Hit the Sesami API in Python
Convert files written in python etc. to pdf with syntax highlighting
How to use SQLite in Python
Workflow to convert formula (image) to python
Convert list to DataFrame with python
Read big endian binary in Python and convert it to ndarray
Try to calculate Trace in Python
How to convert 0.5 to 1056964608 in one shot
Hit the New Relic API in Python to get the server status
Create Gmail in Python without API
Python> list> Convert double list to single list
Hit the web API in Python
How to use Mysql in python