I'm writing some node.js to interact with sensors over a serial port connection. The code for reading the sensor is asynchronous, naturally. In my control code, though, I need to read a sensor, do something based on the value, read again, do something else, etc. To do this, I'm using code like the following self-contained test:
var main = new Main();
main.next();
function* Main()
{
var reading = yield readSensor(this.next.bind(this));
console.log(reading);
var reading = yield readSensor(this.next.bind(this));
console.log(reading);
}
function readSensor(callback)
{
// simulate asynchrounous callback from reading sensor
setTimeout(function sensorCallback() { callback('foo'); }, 100);
}
So, my sequential control code is in a generator which yields to readSensor()
when it needs to get a reading. When the sensor reading is done, it calls the callback, and control returns to the main code. I'm doing it this way because I may need to read from various sensors in different orders depending on previous readings. So, here's the questionable part: I pass this.next.bind(this)
as a callback to the asynchronous read function. The code seems to work when generators are enabled (--harmony_generators
), but I am wondering if there are pitfalls here that I am missing. I'm relatively new to JS, so don't be afraid to point out the obvious :)
I haven't studied ES6 generators in depth, but having a generator pass its own .next
to another function as a callback doesn't sit well with me. If anything, it could create a situation where readSensor
fails and you have no way to handle the failure, ending up in a deadlock.
I suggest modifying or wrapping readSensor
to return a promise, and then using the technique outlined in this article.
That would allow you to write code like this (verified working in Node v0.12.0):
var Promise = require('q');
var main = async(function* () {
var reading = yield readSensor();
console.log(reading);
reading = yield readSensor();
console.log(reading);
});
main();
function readSensor() {
return Promise.delay(2000).thenResolve(Math.random() * 100);
}
/***********************************************************
* From here down, *
* boilerplate async() function from article linked above *
***********************************************************/
function async(makeGenerator){
return function () {
var generator = makeGenerator.apply(this, arguments);
function handle(result){
// result => { done: [Boolean], value: [Object] }
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value).then(function (res){
return handle(generator.next(res));
}, function (err){
return handle(generator.throw(err));
});
}
try {
return handle(generator.next());
} catch (ex) {
return Promise.reject(ex);
}
}
}
As loganfsmyth notes below, Q already provides a Q.async()
method that provides the functionality of this async()
function, and possibly other promise libraries do as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With