@thenewdaniels

Почему моя асинхронная функция останавливает всю программу?

В моём ТГ боте где есть вот такая связка, которая запускает четыре последовательные функции, привязанные каждая к своему отдельному гугл доку:

async def mainGoogleDoc():
    try:
        text1 = await userListInGoogle()
        text2 = await secondStageInList()
        text3 = await thirdStageInList()
        text4 = await referalsInList()
        print(text1,text2,text3,text4)
        if text1 == "DONE" and text2 == "DONE" and text3 == "DONE" and text4 == "DONE":
            text = "DONE"
            return text
    except Exception as e:
        text = "Google Docs Error"
        print(f"Google Docs Error {e}")
        return text


Эта функция вызывается где то в течение кода:
await mainGoogleDoc()

Как только боту поступает задача выполнить эту функцию, бот прекращает обрабатывать все поступающие запросы. Хендлер на aiogram не ловит сообщения. Все отправленные сообщения будут обработаны только после того, как выполнится функция
mainGoogleDoc()
.
Пример одной из задач:
async def referalsInList():
    try:
        project_dir = os.path.dirname(os.path.dirname(__file__))

        db_path = os.path.join(project_dir, 'db', 'database.db')
        cred_path = os.path.join(project_dir, 'utils', 'credentials.json')

        connect = sqlite3.connect(db_path, check_same_thread=False)
        cursor = connect.cursor()
        gs = gspread.service_account(filename=cred_path)  # подключаем файл с ключами и пр.
        sh = gs.open_by_key('TABLE_ID')  # подключаем таблицу по ID
        worksheet = sh.get_worksheet(0)
        cursor.execute("SELECT user_phone, invites FROM userlist WHERE invites > 0")
        users_list = cursor.fetchall()

        rows = worksheet.row_count
        cols = worksheet.col_count

        cell_list = worksheet.range(1, 1, rows, cols)
        for cell in cell_list:
            cell.value = ''
        worksheet.update_cells(cell_list)

        worksheet.append_row(['ID', 'Телефон', 'Количество приглашённых'])

        i = 1
        for row in users_list:
            user_phone = await phoneToRead(str(row[0]))
            invites = str(row[1])
            worksheet.append_row([str(i), user_phone, invites])
            i = i + 1

        worksheet.client.session.close()
        connect.close()
        text = 'DONE'
        return text
    except Exception as s:
        text = f'Ошибка при добавлении данных userlist в таблицу: {s}'
        return text


Я не думаю, что проблема связана со способом вызова этой функции, но для точности добавлю сюда то, как именно она вызывается:

@router.message(adminMenu.quizSettings)
async def cmd_quiz_settings(message: Message, state: FSMContext):
    if message.text.lower() == 'обновить "google doc"':
        await message.answer('Обновляем данные...')
        if await mainGoogleDoc() == "DONE":
            await message.answer('Гугл док успешно обновлён до актуальных данных') 
        else:
            await message.answer('Произошла ошибка при обновлении данных в ГУГЛ ДОК')


Итак вопрос. Как исправить заморозку всего кода на время выполнения этих задач, учитывая то, что все функции асинхронные? Или может быть проблема в чём то другом?
  • Вопрос задан
  • 168 просмотров
Решения вопроса 3
Mike_Ro
@Mike_Ro Куратор тега Python
Python, JS, WordPress, SEO, Bots, Adversting
Хотя Вы и используете асинхронный код выше (в telegram боте), Ваши функции, которые работают с google sheets и бд, работают синхронно. В этом случае, даже если Вы оборачиваете их в await, то они чудесным образом не станут асинхронными и все равно заблокируют исполнение асинхронного eventloop, пока не завершат свою работу.

Чтобы решить Вашу проблему, необходимо использовать библиотеки, которые умеют в асинхронную работу, бд - aiosqlite, а с google sheets - хз, вроде подходит gspread_asyncio.
Ответ написан
Комментировать
@Everything_is_bad
учитывая то, что все функции асинхронные?
а с чего ты решил что они все асинхронные? С базой ты синхронно работаешь, а у gspread поди синхронные http запросы (это и будет основное место блокирови). Ищи асинк версии этих библиотек или запускай синхронный код в run_in_executor
Ответ написан
Комментировать
@thenewdaniels Автор вопроса
Всем спасибо за помощь, вот связка которая решила мою проблему:
Если нет желания искать или подобная асинхронная версия библиотеки отсутствует, можно использовать следующую запись:
# Функция для запуска синхронного кода в run_in_executor
async def run_sync_in_executor(sync_func):
    loop = asyncio.get_event_loop()
    return await loop.run_in_executor(None, sync_func)

# Измененная основная функция для запуска синхронного кода в run_in_executor
async def mainGoogleDoc():
    try:
        text1 = await run_sync_in_executor(userListInGoogle)
        text2 = await run_sync_in_executor(secondStageInList)
        text3 = await run_sync_in_executor(thirdStageInList)
        text4 = await run_sync_in_executor(referalsInList)
        print(text1,text2,text3,text4)
        if text1 == "DONE" and text2 == "DONE" and text3 == "DONE" and text4 == "DONE":
            text = "DONE"
            return text
    except Exception as e:
        text = "Google Docs Error"
        print(f"Google Docs Error {e}")
        return text


Перед этим я убрал для каждой из функции подпись async def.... сделав её синхронной
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы