CodeGym /Cursos /Python SELF ES /Métodos Asíncronos

Métodos Asíncronos

Python SELF ES
Nivel 25 , Lección 2
Disponible

3.1 Módulo asyncio

Ya nadie crea sus propios hilos para tareas asíncronas. Bueno, se pueden crear, pero eso se considera demasiado de bajo nivel y solo lo usan los desarrolladores de frameworks. Y eso, solo cuando es absolutamente necesario.

Ahora están de moda la programación asíncrona, los operadores async/await y las corutinas con tareas. Pero vamos por partes…

Un poco de historia

Inicialmente, en Python para resolver tareas de programación asíncrona se utilizaban corutinas, basadas en generadores. Luego, en Python 3.4, apareció el módulo asyncio (a veces se escribe como async IO), en el que se implementan los mecanismos de programación asíncrona. En Python 3.5 apareció la construcción async/await.

Ahora un poco de información introductoria. Primero te explicaré brevemente sobre todas estas cosas, luego con más detalles, y después aún más. De otra forma no se puede hacer, ya que casi todos funcionan en conjunto, y explicar detalladamente el funcionamiento de una entidad sin referencias a otras no es posible.

Módulo asyncio

El módulo asyncio está diseñado para escribir programas asíncronos, proporcionando la capacidad de ejecutar tareas en paralelo. Soporta operaciones de entrada/salida asíncronas, temporizadores, sockets, ejecución de corutinas y multiprocesamiento, trabajando en uno o más bucles de eventos.

Corutinas (Coroutines)

Las corutinas son funciones asíncronas, definidas con la palabra clave async def. Las corutinas permiten suspender su ejecución con la palabra clave await, lo que permite que otras corutinas se ejecuten en ese tiempo.


import asyncio

# Definición de una función asíncrona (corutina)
async def main():
    print('Hello ...')
    # Suspendemos la ejecución por 1 segundo
    await asyncio.sleep(1)
    print('... World!')

# Ejecución de la función asíncrona main() en el bucle de eventos
asyncio.run(main())

Bucle de Eventos (Event Loop)

El bucle de eventos gestiona la ejecución de corutinas, tareas y otras operaciones asíncronas. La llamada asyncio.run() inicia el bucle de eventos y ejecuta la corutina hasta su finalización.


import asyncio

async def main():
    print('Hello ...')
    await asyncio.sleep(1)
    print('... World!')

# Obtener el bucle de eventos actual
loop = asyncio.get_event_loop()
# Ejecutar la corutina hasta que finalice
loop.run_until_complete(main())
# Cerrar el bucle de eventos después de completar todas las tareas
loop.close()

Tareas (Tasks)

Las tareas permiten ejecutar corutinas en paralelo. Se crean con asyncio.create_task() o asyncio.ensure_future().


import asyncio

# Definición de una corutina que se ejecutará con retraso
async def say_after(delay, what):
    # Suspendemos la ejecución durante el tiempo especificado
    await asyncio.sleep(delay)
    print(what)

# Corutina principal
async def main():
    # Crear tareas para la ejecución paralela de corutinas
    task1 = asyncio.create_task(say_after(1, 'hello'))
    task2 = asyncio.create_task(say_after(2, 'world'))
    
    # Esperar la finalización de ambas tareas
    await task1
    await task2

# Ejecución de la corutina principal
asyncio.run(main())

Futuros (Futures)

Los objetos Future representan los resultados de operaciones asíncronas, que estarán disponibles en el futuro. Por ejemplo, se utilizan para esperar la finalización de una tarea asíncrona.


import asyncio

# Definición de una corutina que simula una tarea larga
async def long_running_task():
    print('Task started')
    # Suspendemos la ejecución por 3 segundos
    await asyncio.sleep(3)
    print('Task finished')
    return 'Result'

# Corutina principal
async def main():
    # Crear un futuro para esperar la finalización de la tarea
    future = asyncio.ensure_future(long_running_task())
    # Esperar la finalización de la tarea y obtener el resultado
    result = await future  
    print(f'Task result: {result}')

# Ejecución de la corutina principal
asyncio.run(main())

3.2 Función Asíncrona — async def

Una función asíncrona se declara igual que una normal, solo que antes de la palabra clave def se debe escribir la palabra async.


async def NombreFuncion(parámetros):
    código de la función

Una función asíncrona se declara como una normal, se llama como una normal, pero el resultado que devuelve es diferente. Si llamas a una función asíncrona, no devolverá el resultado, sino un objeto especial — corutina.

Puedes incluso verificar esto:


import asyncio

async def main():
    print("Hello World")
            
# Llamada a una función asíncrona que devuelve una corutina
result = main()
# Comprobar el tipo de resultado
print(type(result)) # <class 'coroutine'>

¿Qué sucede? Cuando marcas una función con la palabra async, en realidad le estás añadiendo un decorador que hace algo parecido a esto:


def async_decorator(func):
    # Crear un objeto Task
    task = Task()
    # Pasar nuestra función func para que la ejecute
    task.target = func  
    # Añadir el objeto task a la cola de tareas — Event Loop
    eventloop.add_task(task)  
    # Devolver el objeto task
    return task 

Y tu código se parece a esto:


import asyncio

@async_decorator
def main():
    print("Hello World")
            
result = main()
print(type(result)) # <class 'coroutine'>

El sentido de esta analogía es el siguiente:

Cuando llamas a una función asíncrona, se crea un objeto especial Task, que ejecutará tu función, pero en el futuro. Puede que en 0.0001 seg, o tal vez en 10.

Este objeto task se te devuelve inmediatamente como resultado de la llamada a tu función asíncrona. Esto es la corutina. Tu función asíncrona, posiblemente, ni siquiera ha comenzado a ejecutarse, pero el objeto task (corutina) ya lo tienes.

¿Para qué necesitas este task (corutina)? No puedes hacer mucho con él, pero puedes hacer 3 cosas:

  • Esperar hasta que la función asíncrona se ejecute.
  • Esperar hasta que la función asíncrona termine de ejecutarse y obtener el resultado de la ejecución de la función desde la corutina.
  • Esperar hasta que se ejecuten 10 (cualquier número) funciones asíncronas.

Te explicaré cómo hacerlo más adelante.

3.3 Operador await

La mayor parte de las acciones con una corutina comienza con "esperar a que la función asíncrona se ejecute". Por eso para esta acción tenemos un operador especial await.

Solo necesitas escribirlo antes de la corutina:


await corutina

O directamente antes de llamar a la función asíncrona:


await funcion_asincrona(argumentos)

Cuando Python encuentra en el código el operador await, suspende la ejecución de la función actual y espera hasta que la corutina se ejecute, es decir, hasta que la función asíncrona a la que se refiere la corutina termine.

¡Importante! El operador await se utiliza solo dentro de una función asíncrona para suspender la ejecución hasta que se complete otra corutina u operación asíncrona.

Esto se hace para simplificar el proceso de cambio entre llamadas a funciones asíncronas. Tal llamada await es básicamente una declaración de "vamos a esperar aquí un tiempo indeterminado, ocupémonos de ejecutar otras funciones asíncronas".

Ejemplo:


import asyncio

# Definición de una función asíncrona
async def async_print(text):
    print(text)
        
# Función asíncrona principal
async def main():
    # Usar await para esperar la ejecución de la función asíncrona
    await async_print("Hello World")
        
# Ejecutar el bucle principal de eventos y ejecutar la corutina main()
asyncio.run(main()) #ejecuta la función asíncrona

En realidad, el operador await funciona de forma aún más astuta: también devuelve el resultado de la ejecución de la función asíncrona a la que fue llamada.

Ejemplo:


import asyncio

# Definición de una función asíncrona que suma dos números
async def async_add(a, b):
    return a + b
        
# Función asíncrona principal
async def main():
    # Usar await para obtener el resultado de async_add
    sum = await async_add(100, 200)
    print(sum)
        
# Ejecutar el bucle principal de eventos y ejecutar la corutina main()
asyncio.run(main()) #ejecuta la función asíncrona

Entonces, para resumir, el operador await:

  • Suspende la función asíncrona actual hasta que se complete otra corutina u operación asíncrona.
  • Devuelve el resultado de la operación asíncrona o corutina.
  • Se puede utilizar solo dentro de una función asíncrona.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION