Можно ли запустить асинхронный скрипт Python в стиле машинописного текста

На данный момент у меня есть скрипт python, который делает http-запрос к микросервису. Запрос занимает в среднем 3 секунды.

Это мой скрипт Python в обобщенном виде.

def main():
  response = request_to_MS(url)

  # This process does not need the response of the microservice.
  some_process()

  # This is where i actually need a response from the microservice
  do_something_with_response(response)


main()

Я хотел бы, чтобы мой скрипт мог продолжить работу с кодом и дождаться ответа на запрос позже, аналогично машинописному тексту.

/**
 * I'd like to write this kind of code in python.
 */
function get_data(): Promise<string>{
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('This is resolved');
    })
  })
}

async function main(){
  const data = get_data();
  console.info('Data variable stores my promise ', data);
  // Some process
  [1, 2, 3, 4, 5, 6 ,7 ,8].forEach((x: number) => console.info(x));
  // I need the promise value here
  console.info('En el await', (await data).length)
}


void main();

В основном то, что я ищу, это то, что время, необходимое для завершения выполнения процесса, и время отклика микросервиса перекрываются, что позволяет в целом улучшить время отклика.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
223
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Сделайте request_to_MS(url) сопрограммой async def, запланируйте ее как задачу с response = asyncio.create_task(request_to_MS(url)).

Он начнет выполняться. Теперь можно продолжать бегать some_process(). Когда вам нужно response, просто сделайте

do_something_with_response(await response)

редактировать: вышеизложенное будет работать только в том случае, если main также является async def, так как вы можете использовать await только в асинхронной функции. Вместо того, чтобы звонить main(), звоните asyncio.run(main()).

Всего:

async def request_to_MS(url):
    await asyncio.sleep(3)
    return 'some internet data'

async def main():
    response = asyncio.create_task(request_to_MS(url))
    # response is now a Task

    some_process()

    # if response isn't done running yet, this line will block until it is
    do_something_with_response(await response)

asyncio.run(main())

Важное предостережение: some_process не обязательно должна быть сопрограммой, но если это блокирующая функция (либо процессором, либо вводом-выводом), она никогда не даст никаких циклов для запуска response. Если он блокируется через ввод-вывод, подумайте о том, чтобы сделать его также сопрограммой. Если нет асинхронной поддержки для любых низкоуровневых операций ввода-вывода, которые он выполняет, или если он привязан к ЦП, рассмотрите возможность использования run_in_executor asyncio.

В моем случае у меня есть функция блокировки ввода-вывода request_to_MS и функция блокировки процессора some_process. Как было предложено, я использовал run_in_executor для запуска some_process и, похоже, получил ожидаемые результаты. Теперь возникает вопрос, почему бы не использовать run_in_executor или create_task в обоих случаях?

Nicolas Cardenas 31.05.2019 03:32

Знаете ли вы, как поделиться моим примером кода в этом посте? Это сделано для того, чтобы любой, кто зайдет на этот пост, мог увидеть окончательный результат.

Nicolas Cardenas 31.05.2019 03:35

Этот скрипт работает на ограниченной машине с 1 процессором, будет ли он работать так же, когда я загружу его в рабочую среду?

Nicolas Cardenas 31.05.2019 03:58
run_in_executor использует пул потоков, поэтому вы можете запускать все, что угодно, но ваша производительность будет не лучше, чем при использовании потоков. Если это нацелено на машину с одним процессором, использование нескольких потоков вообще не улучшит вашу производительность. Вместо этого asyncio — это парадигма «совместной многозадачности». Все сопрограммы выполняются в асинхронном цикле, который выполняется в одном потоке, избегая накладных расходов на потоки, но при этом выполняя многозадачность. Подвох: тот, кто создал используемую вами библиотеку ввода-вывода, должен явно поддерживать asyncio.
Tom Lubenow 31.05.2019 17:01

Итак, общее эмпирическое правило: если вы можете запустить его как задачу, вы должны это сделать, так как это будет иметь минимальные накладные расходы. Если операция ввода-вывода не поддерживает низкоуровневую асинхронную поддержку, вам придется run_in_executor что приведет к немного большим накладным расходам. Я бы почитал об асинхронности Python, потому что его поведение может иногда немного сбивать с толку, но как только вы «поймете его», я думаю, что это имеет большой смысл. Что касается вашего кода, я бы просто отредактировал его в исходном вопросе в конце, и я был бы признателен, если бы вы приняли мой ответ.

Tom Lubenow 31.05.2019 17:10

Другие вопросы по теме