Implementing coroutine in NodeJS
The output of iojs led me to study functions that have already become stable in v8, in particular native promises and generators that can be turned into coroutines. I was surprised that on Habré there is no article devoted to how to make one more implementation of coroutine through generators and understand what actually happens in co / bluebird. To start using without fear of magic coroutine please under the cat.
In particular, coroutines give us the opportunity to write similar code:
It turns out quite briefly and clearly. Instead of timeout, there can be any other function that knows how to promise, for example, going for data to the database and returning data directly to the same function, rather than to then / callback, which is quite convenient, because allows and cancel execution of sequential asynchronous functions more conveniently than is possible in the promise chain.
The whole point of the question is what this generatorWrapper looks like. And this is my reasoning in this regard.
This implementation is ideological for understanding how it all works, but the ideology is about the same in co and bluebird.
Of course, co and bluebird.coroutine are full-fledged implementations , they have parallel processing and the possibility of advanced processing, including work with other generators.
The code shows that the generator in JS, although it allows you to write code of the form as coroutines, is not really intended for this. The generator, excuse me for captainhood and tautology, first of all, the generator and it’s most convenient to do lazy calculations on it, and using it in this way is not exactly its intended purpose, although I did not notice any performance degradation in my tests.
On the other hand, it becomes clear when looking at future standards such as await / async that everything is not so far in principle, you can safely use current generator-based coroutines using iojs + co / bluebird without worrying about product stability. And considering that all correct implementation of corutin returns a native promise, then all this is completely compatible with future standards.
To summarize: calmly put and use it in iojs. In the code written on promises all this gets up without any changes. If you want to add something to the functionality, you can look into the same co and add something of your own, there is nothing wrong with this processing. If you want to make async race, do not deny yourself this, but then you have to do it in another function on promises, not a generator, or come up with your own data format, otherwise feel free with generators, they are now the same part of the standard as everything else .
PS Sorry for the raw material, I hope for your help in improving it.
Coroutine is a function (program) that has several input points. You can stop it at a certain point, do something else, and then return to that point again and continue with new data (not necessarily with new data but with the same state as it was). Fiber is one of the coroutine implementations for nodejs (e.g. node-fibers ), but now we are talking about implementing without writing a C ++ plugin or metaprogramming, only with native Javascript methods.
In particular, coroutines give us the opportunity to write similar code:
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);
It turns out quite briefly and clearly. Instead of timeout, there can be any other function that knows how to promise, for example, going for data to the database and returning data directly to the same function, rather than to then / callback, which is quite convenient, because allows and cancel execution of sequential asynchronous functions more conveniently than is possible in the promise chain.
The whole point of the question is what this generatorWrapper looks like. And this is my reasoning in this regard.
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());//отдаем результат из генератора в функцию обертка
});
}
This implementation is ideological for understanding how it all works, but the ideology is about the same in co and bluebird.
Of course, co and bluebird.coroutine are full-fledged implementations , they have parallel processing and the possibility of advanced processing, including work with other generators.
The code shows that the generator in JS, although it allows you to write code of the form as coroutines, is not really intended for this. The generator, excuse me for captainhood and tautology, first of all, the generator and it’s most convenient to do lazy calculations on it, and using it in this way is not exactly its intended purpose, although I did not notice any performance degradation in my tests.
On the other hand, it becomes clear when looking at future standards such as await / async that everything is not so far in principle, you can safely use current generator-based coroutines using iojs + co / bluebird without worrying about product stability. And considering that all correct implementation of corutin returns a native promise, then all this is completely compatible with future standards.
To summarize: calmly put and use it in iojs. In the code written on promises all this gets up without any changes. If you want to add something to the functionality, you can look into the same co and add something of your own, there is nothing wrong with this processing. If you want to make async race, do not deny yourself this, but then you have to do it in another function on promises, not a generator, or come up with your own data format, otherwise feel free with generators, they are now the same part of the standard as everything else .
PS Sorry for the raw material, I hope for your help in improving it.