Имплементация coroutine в NodeJS
Выход iojs сподвиг меня на изучение функций, которые уже стали стабильными в v8, в частности нативным промисам и генераторам, которые можно превращать в корутины. Удивился, что на Хабре нету статьи посвященной тому как самому сделать еще одну имплементацию coroutine через генераторы и понять, что же на самом деле происходит в co/bluebird. Чтобы начать пользоваться без страха перед магией coroutine прошу под кат. Корутины (coroutine) — это функция (программа) имеющая несколько входных точек. Ее можно остановить в определенной точке, выполнить что-то другое, а затем снова вернуться в эту точку и продолжить с новыми данными (необязательно с новыми данными, но с тем же состоянием, что и было). Fiber является одной из имплементацией корутин для nodejs (например node-fibers), но сейчас мы говорим об имплементации без написания плагина на C++ или метапрограммирования, только нативными методами Javascript.
В частности корутины нам дают возможность писать подобный код:
function timeout (ms, msg){ return new Promise (function (resolve, reject){ setTimeout (function (msg){ console.log (msg); resolve (msg); }, ms, msg); }); } var makeJob = function *(){ yield timeout (1000, 1); yield timeout (1000, 2); var user = yield getUser (); return user.info; }; generatorWrapper (makeJob); Получается довольно кратко и понятнее. Вместо timeout может быть любая другая функция умеющая в promise’ы, например ходящая за данными в базу и возвращающая данные прямо в эту же функцию, а не в then/callback, что довольно удобно, т.к. позволяет и отменять исполнение последовательных асинхронных функций более удобно, чем это возможно в promise chain.Вся суть вопроса как выглядит этот generatorWrapper. И это мои рассуждения на этот счет.
function async (gen){ var instance = gen (); //создаем инстанс генератора, по которому будем итерировать return new Promise (function (resolve, reject){ //функция обертка для приходящих данных из корутины function next®{ if (r.done) { resolve (r.value);//если все закончилось просто отдаем resolve’им результат генератора. } if (typeof (r.value.then)=='function')//проверка через duck typing, т.к. есть много Promise совместимых библиотек { r.value.then (function (someRes){ next (instance.next ());//вызываем next снова на новых данных. }, function (e){ reject (e); }); } else { console.log (r.value); next (instance.next ()); } } next (instance.next ());//отдаем результат из генератора в функцию обертка }); } Эта имплементация идеологическая для понимания как оно все работает, но идеология примерно такая же в co и bluebird.Конечно полноценными имплементациями является co и bluebird.coroutine, в них есть паралельная обработка и возможность расширенной обработки в том числе работа с другими генераторами.По коду видно, что генератор в JS, хоть и позволяет писать код вида как корутины, но на самом деле не предназначен для этого. Генератор, извините за капитанство и тавтологию, прежде всего генератор и наиболее удобно на нем все таки делать ленивые вычисления, а использование его подобным образом не совсем его прямое назначение, хотя по моим тестам никаких деградации производительности не заметил.С другой стороны становится ясно глядя на будущие стандарты типа await/async, что все в принципе не так уж далеко, уже текущими корутинами на базе генераторов можно спокойно пользоваться используя iojs + co/bluebird не опасаясь за стабильность продукта. А учитывая, что все правильные имплементации корутин возвращают нативный promise то все это вполне совместимо с грядущими стандартами.
Резюмирую: спокойно ставьте и используйте это в iojs. В код написанный на промисах все это встает без каких либо изменений. Если что-то хочется добавить к функционалу можете поглядеть в тот же co и дописать что-то свое, там нет ничего страшного в этой обработке. Если хочется сделать async race не отказывайте себе в этом, но придется тогда это делать в другой функции на promise’ах, не генераторе, или придумать свой формат данных, в остальном feel free с генераторами, они теперь такая же часть стандарта как и все остальное.
P.S. Извините за сырой материал, надеюсь на вашу помощь по его улучшению.