Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@mikermcneil
Last active October 15, 2017 23:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikermcneil/d781573e2e30d6963b5586ad69663979 to your computer and use it in GitHub Desktop.
Save mikermcneil/d781573e2e30d6963b5586ad69663979 to your computer and use it in GitHub Desktop.
Promises combined with es8 async/await: How can we cleanly implement early return?
var machine = require('machine');
var doSomething = machine({
exits: {
notFound: {description: 'IT was not found'}
},
fn: (inputs, exits) => {
// throw new Error('whoops');
return exits.notFound();
// return exits.success(3);
}
});
// Now imagine calling this....
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// So naively, you'd do .catch() here. But there's a problem-- it ALWAYS converges...
// Which means it's not actually like a functional equivalent to a real JavaScript try/catch
// block.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// So to use it, we have to do one of these annoying things:
// .catch((err)=>{
// console.error('unexpected error:',err);
// // -EITHER-
// // Option A:
// // Call `exits.error(err);` and then `return;` (or just `return exits.error(err);`) but then
// // carefully checking to make sure you don't accidentally do anything else below (because it
// // would converge)
// // -OR-
// // Option B:
// // Just `throw err;`, flaverring it first if necessary. This option is better, but still kind
// // of annoying compared to how easy it is with traditional asynchronous code. In this case,
// // it only works because you don't even call `exits`. And this is less good for a Sails/Express
// // app, where you might actually care about doing stuff with `res`.
// });
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// But there's an easier way: `.done()`.
// It's just that it's actively railed against. It's non-standard, which isn't a big deal really.
// But the fact that it's been more or less arbitrarily considered an anti-pattern means that it's
// a no-go.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// So I guess for now we'll just recommend good ole try/catch, like you'd do with a synchronous machine...
// ```
// var result;
// try {
// result = await doSomething();
// }
// catch (e) {
// if (e.code === 'notFound'){ return exits.notFound(); }
// return exits.error(e);
// }
//
// // ...(onwards)
// ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// The problem:
(async function(inputs, exits) {
var result = await doSomething()
.catch ('missing', ()=>{ return exits.notFound(); })
.catch ((e)=>{ return exits.error(e); });
// THIS WOULD NOT WORK AS EXPECTED BECAUSE, NO MATTER WHAT,
// THE FOLLOWING CODE WOULD RUN. ALTERNATIVELY, WE COULD THROW
// FROM INSIDE THE CATCH HANDLERS, BUT THAT WOULD PREVENT US FROM
// PROPERLY CALLING EXITS.NOTFOUND(), SINCE IT WOULD JUST HIT THE CATCHALL.
console.log('Success!!',result);
return exits.success();
})({}, {
error: function (err){
console.error('Hit proper error outlet', err);
},
notFound: function (){
console.error('Hit proper notFound outlet');
},
success: function(result){
console.error('Hit proper success outlet', result);
}
}).catch((e)=>{
console.error('Hit unhandled rejection catchall', e);
});
// The "right" solution
// (This works just fine, as long as you remember to do it)
(async function(inputs, exits) {
var result;
try {
result = await doSomething();
} catch (e) {
if (e.code === 'notFound') {
return exits.notFound();
}
return exits.error(e);
}
console.log('Success!!',result);
return exits.success();
})({}, {
error: function (err){
console.error('Hit proper error outlet', err);
},
notFound: function (){
console.error('Hit proper notFound outlet');
},
success: function(result){
console.error('Hit proper success outlet', result);
}
}).catch((e)=>{
console.error('Hit unhandled rejection catchall', e);
});
// A possible solution that would still allow for mixing and matching:
(async function(inputs, exits) {
var result = await doSomething()
.catchAndRelease('missing', ()=>{ return exits.notFound(); })
.catchAndRelease((e)=>{ return exits.error(e); });
// This would solve the problem.
console.log('Success!!',result);
return exits.success();
})({}, {
error: function (err){
console.error('Hit proper error outlet', err);
},
notFound: function (){
console.error('Hit proper notFound outlet');
},
success: function(result){
console.error('Hit proper success outlet', result);
}
}).catch((e)=>{
console.error('Hit unhandled rejection catchall', e);
});
@mikermcneil
Copy link
Author

But see also https://gist.github.com/mikermcneil/c1bc2d57f5bedae810295e5ed8c5f935

My takeaway was that this "avast" approach adds too much complexity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment