genny
genny copied to clipboard
Seeking advice to avoid anti-patterns with promises
I'm finding myself wanting to wrap longer genny functions containing several async functions with a bluebird promise that resolves or rejects at the end of the genny function.
For some reason this doesn't feel right at all. Part of the reason I'm trying to use generators in the first place is to replace promises to reduce memory usage and improve readability. I think I'm missing the bigger picture.
Am I supposed to only have one "master" genny function in my application that yields a number of child non-genny generator functions instead? Will errors and stack traces still propagate?
Hoping to avoid an anti-pattern early. Thanks in advance!
If using promises at all, I would recommend using only promises via bluebird. It has long stack traces, as well as very elaborate API promisification functionality that makes it easy to convert foreign callback-based libraries.
genny is only meant for node style callbacks.
p.s. Having one master genny function that yields non-genny generator functions might be a pretty good idea: https://spion.github.io/posts/es7-async-await-step-in-the-wrong-direction.html
I am also interested in this topic. After experimenting with the migration of an Express based app to Promises and Generators, I found the code far more readable using Generators in the routes. It allows you to easily maintain for/while loops with yielded operations in the loop, although you have to be careful to stick to old fashioned loops and avoid things like Array.map()
as you mentioned in your article.
The real bonus for me though is how errors are propagated when using genny.middleware()
to wrapper the Express routes. Because they effectively have a try/catch around them which sends errors using next(err)
, it means we can avoid having a bunch of messy if (err instanceof foo)
statements in the catch clause and handle it via the normal Express error chain.
For example, I have a route mounted on /api
, which mounts several sub routes and has the following handler added at the end to catch anything that was unexpected:
// final error handler
router.use(function(err, req, res, next) {
logger.error(err.stack);
res.status(err.status || 500).json({ error : err.message });
// throw err?
});
Prior to this handler, or in the sub routes I then setup more specific error handlers for expected errors, e.g.:
router.use(function(err, req, res, next) {
if(err instanceof PackageError)
return res.status(400).json({ error: err.message });
else
next(err);
});
And then in the routes we can let the statements throw exceptions and it all gets nicely handled in the usual Express way. Nice!