Корутинные будни, часть 3 из 3.
Callback hell
Один из способов — вынести часть «после» в отдельную функцию и вместо обычного recv () вызывать умную обёртку, мол, «мне нужны данные, когда они появятся — вызови вот этот метод». Если у вас была локальная переменная — извините, теперь ей место в поле вашего класса, или в замыкании вашей функции (для тех, кто уже умеет в лямбды). В целом, работающий подход, если кто писал на node.js до ES-2015, то там только так и жили. Вот только код разрастается в неприятную сторону, когда цепочка колбеков становится длиннее пяти.
Но мы же не роботы, чтобы вкалывать. Давайте попросим компилятор (он железный, ему всё равно) расщеплять нашу функцию на кусочки, которые можно будет выполнять по одному и ждать данных в промежутках. Это и называется корутины. Они суть просьба для компилятора:
— сделать специальный корутинный объект,
— хранить в этом объекте точку выполнения корутины,
— хранить в этом объекте то, что кажется локальными переменными,
— от этого объекта нужно только действие «выполнись-ка до следующей асинхронной точки», ну и часто любят какое-нибудь взаимодействие на это завязать (yield данных туда-сюда, например).
Вместо заключения
Потоки иногда тоже нужны. Например, потому что в современных операционных системах можно мультиплексировать обращения к сети и всякий IPC (pipes, signals), но не обращения к файловой системе. Попытка сделать`read ()` из обычного файла заблокирует ваш поток, возможно надолго (например, если файловая система — sshfs, а у нас сеть закончилась). Такое можно и нужно выносить в отдельный поток, а лучше пул потоков. Аналогично, бывает пачка вычислений, независимая от основного выполнения — чтобы воспользоваться преимуществом многоядерных процессоров и многопроцессорных систем, понадобятся потоки для разгрузки вычислений на основном.
Но для понимания асинхронного подхода и корутинок лучше забыть про параллельные вычисления. Они тут только мешают. Чтобы обрабатывать десять тысяч подключений одновременно, достаточно последовательных, и очень простых — если не давать себя запутать.
Callback hell
Один из способов — вынести часть «после» в отдельную функцию и вместо обычного recv () вызывать умную обёртку, мол, «мне нужны данные, когда они появятся — вызови вот этот метод». Если у вас была локальная переменная — извините, теперь ей место в поле вашего класса, или в замыкании вашей функции (для тех, кто уже умеет в лямбды). В целом, работающий подход, если кто писал на node.js до ES-2015, то там только так и жили. Вот только код разрастается в неприятную сторону, когда цепочка колбеков становится длиннее пяти.
Но мы же не роботы, чтобы вкалывать. Давайте попросим компилятор (он железный, ему всё равно) расщеплять нашу функцию на кусочки, которые можно будет выполнять по одному и ждать данных в промежутках. Это и называется корутины. Они суть просьба для компилятора:
— сделать специальный корутинный объект,
— хранить в этом объекте точку выполнения корутины,
— хранить в этом объекте то, что кажется локальными переменными,
— от этого объекта нужно только действие «выполнись-ка до следующей асинхронной точки», ну и часто любят какое-нибудь взаимодействие на это завязать (yield данных туда-сюда, например).
Вместо заключения
Потоки иногда тоже нужны. Например, потому что в современных операционных системах можно мультиплексировать обращения к сети и всякий IPC (pipes, signals), но не обращения к файловой системе. Попытка сделать`read ()` из обычного файла заблокирует ваш поток, возможно надолго (например, если файловая система — sshfs, а у нас сеть закончилась). Такое можно и нужно выносить в отдельный поток, а лучше пул потоков. Аналогично, бывает пачка вычислений, независимая от основного выполнения — чтобы воспользоваться преимуществом многоядерных процессоров и многопроцессорных систем, понадобятся потоки для разгрузки вычислений на основном.
Но для понимания асинхронного подхода и корутинок лучше забыть про параллельные вычисления. Они тут только мешают. Чтобы обрабатывать десять тысяч подключений одновременно, достаточно последовательных, и очень простых — если не давать себя запутать.