Last active
October 15, 2017 23:03
-
-
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?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
But see also https://gist.github.com/mikermcneil/c1bc2d57f5bedae810295e5ed8c5f935
My takeaway was that this "avast" approach adds too much complexity