javascriptpythonpromise

What is the Python equivalent of a Promise from JavaScript?


I am trying to understand what would be the best code structure or a design pattern that would do the following:

Have a function named get_data that would start up a socket connection and will start waiting for a specific socket event that would return some data. Once it gets the data, only then the get_data function should be returned.

So in JS it would be very simple like this:

(Keep in mind that this code snippet is an example, it's not meant to be a working code)

const getData = () => {
  return new Promise((resolve, reject) => {
    const socket = socket()
    socket.on("message", (data)=>{
      resolve(data)
    })
    socket.connect("localhost")
  });
}

const parseData = async () => {
  const data = await getData()
  // do something with the data
}

With Python however, I have absolutely no idea how to achieve the same result.

How would I translate it to Python? What strategy would be used here?

The only way I could think of right now is like this:

def get_data():
  socket = socket()
  my_data = None

  @socket.event
  def message(data):
    nonlocal my_data
    my_data = data

  socket.connect("localhost")
  
  while not my_data:
    time.sleep(0.3)
  
  socket.disconnect()
  return my_data

def parse_data():
  data = get_data()
  # do something with the data

Solution

  • If you want to convert a callback-based API into an async API, you’re probably looking for asyncio.Future.

    Modifying your strawman code a bit:

    import asyncio
    
    def get_data():
        # create a future
        future = asyncio.Future()
    
        # create a socket
        socket = socket()
    
        # connect callbacks
        @socket.on("message")
        def on_message(data):
            future.set_result(data)
    
        @socket.on("error")
        def on_error(error):
            future.set_exception(error)
    
        # create a background task that will communicate with the socket
        # and invoke the callbacks
        @asyncio.create_task
        async def communicate():
            socket.connect('localhost')
            socket.communicate()
    
        # return the future
        return future
    

    Calling get_data will return an object you can await on just like async function calls:

    async def parse_data():
        data = await get_data()
        # process further