Выход 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(r){
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. Извините за сырой материал, надеюсь на вашу помощь по его улучшению.
Автор: KlonD90