Skip to content

Instantly share code, notes, and snippets.

@mikermcneil
Last active August 4, 2016 22:04
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/755a2ae7cc62d9a59656ab3ba9076cc1 to your computer and use it in GitHub Desktop.
Save mikermcneil/755a2ae7cc62d9a59656ab3ba9076cc1 to your computer and use it in GitHub Desktop.
I've answered a lot of questions lately about asynchronous (non-blocking) vs. synchronous (blocking) function declarations (implementation) and asynchronous (non-blocking) vs. synchronous (blocking) invocations (userland). This is a set of crude representative examples that aims to shed some light on the underlying concepts at work.
// ╔═╗╔═╗╦ ╦╔╗╔╔═╗ ┌─┌┐┌┌─┐┌┐┌ ┌┐ ┬ ┌─┐┌─┐┬┌─┬┌┐┌┌─┐─┐
// ╠═╣╚═╗╚╦╝║║║║ │ ││││ ││││───├┴┐│ │ ││ ├┴┐│││││ ┬ │
// ╩ ╩╚═╝ ╩ ╝╚╝╚═╝ └─┘└┘└─┘┘└┘ └─┘┴─┘└─┘└─┘┴ ┴┴┘└┘└─┘─┘
// ┬ ┬┌─┐
// ───└┐┌┘└─┐───
// └┘ └─┘o
// ╔═╗╦ ╦╔╗╔╔═╗╦ ╦╦═╗╔═╗╔╗╔╔═╗╦ ╦╔═╗ ┌─┌┐ ┬ ┌─┐┌─┐┬┌─┬┌┐┌┌─┐─┐
// ╚═╗╚╦╝║║║║ ╠═╣╠╦╝║ ║║║║║ ║║ ║╚═╗ │ ├┴┐│ │ ││ ├┴┐│││││ ┬ │
// ╚═╝ ╩ ╝╚╝╚═╝╩ ╩╩╚═╚═╝╝╚╝╚═╝╚═╝╚═╝ └─└─┘┴─┘└─┘└─┘┴ ┴┴┘└┘└─┘─┘
// ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬
// ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─ ┌┼─
// └┘ └┘ └┘ └┘ └┘ └┘ └┘ └┘ └┘ └┘ └┘ └┘ └┘ └┘ └┘ └┘ └┘ └┘
// ╔═╗╔╗╔ ╔╦╗╔═╗╔═╗╦ ╔═╗╦═╗╔═╗╔╦╗╦╔═╗╔╗╔ ┌─┬┌┬┐┌─┐┬ ┌─┐┌┬┐┌─┐┌┐┌┌┬┐┌─┐┌┬┐┬┌─┐┌┐┌─┐
// ╠╣ ║║║ ║║║╣ ║ ║ ╠═╣╠╦╝╠═╣ ║ ║║ ║║║║ │ ││││├─┘│ ├┤ │││├┤ │││ │ ├─┤ │ ││ ││││ │
// ╚ ╝╚╝ ═╩╝╚═╝╚═╝╩═╝╩ ╩╩╚═╩ ╩ ╩ ╩╚═╝╝╚╝ └─┴┴ ┴┴ ┴─┘└─┘┴ ┴└─┘┘└┘ ┴ ┴ ┴ ┴ ┴└─┘┘└┘─┘
// ┬ ┬┌─┐
// ───└┐┌┘└─┐───
// └┘ └─┘o
// ╔═╗╔╗╔ ╦╔╗╔╦ ╦╔═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔ ┌─┬ ┬┌─┐┌─┐┬─┐┬ ┌─┐┌┐┌┌┬┐─┐
// ╠╣ ║║║ ║║║║╚╗╔╝║ ║║ ╠═╣ ║ ║║ ║║║║ │ │ │└─┐├┤ ├┬┘│ ├─┤│││ ││ │
// ╚ ╝╚╝ ╩╝╚╝ ╚╝ ╚═╝╚═╝╩ ╩ ╩ ╩╚═╝╝╚╝ └─└─┘└─┘└─┘┴└─┴─┘┴ ┴┘└┘─┴┘─┘
//
// I've answered a lot of questions lately about asynchronous (non-blocking)
// vs. synchronous (blocking) function declarations (implementation) and
// asynchronous (non-blocking) vs. synchronous (blocking) invocations (userland).
//
// This is a set of crude representative examples that aims to shed some light
// on the underlying concepts at work.
// -------------------------------------------------------
// api/services/SomeService.js
module.exports = {
/**
* A synchronous function declaration; in this case, this is a helper within a service.
*
* @required {Dictionary} newUserProps
* @return {String} [a short message]
* @throws {Error?} [if something goes wrong]
*/
signUpSync: function (opts){
var newUserId = createUserSync(opts.newUserProps);
sendWelcomeEmailSync({ userId: newUserId });
return 'hello';
}
}
// (note that no explicit try/catch is necessarily required above since
// the default behavior of throwing any error up to the caller just works
// automatically.. unless you need to negotiate errors programatically.
// To put it another way, the code above is not going to crash your process.)
// -------------------------------------------------------
// Userland of synchronous function
function (req, res) {
var msg = SomeService.signUpSync({
newUserProps: {}
});
return res.json({
message: msg
});
}
// -------------------------------------------------------
// -------------------------------------------------------
// api/services/SomeService.js
module.exports = {
/**
* An asynchronous function declaration; e.g. a helper in a service.
*
* @required {Dictionary} newUserProps
* @async
* @callback [done]
* @param {Error?} [if something goes wrong]
* @param {String} [a short message]
*/
signup: function (opts, done){
createUser({}, function (err) {
if (err) { return done(err); }
sendWelcomeEmail({ userId: newUserId }, function (err) {
if (err) { return done(err); }
return done(undefined, 'hello');
});
});
}
}
// -------------------------------------------------------
// Userland of asynchronous function
function (req, res) {
var msg = SomeService.signUp({
newUserProps: {}
}, function (err, result) {
if (err) { return res.serverError(err); }
return res.json({
message: msg
});
});
}
// -------------------------------------------------------
// -------------------------------------------------------
// re try/catch: This is the implicit structure of
// synchronous code which is hidden from us automatically
// via the automatic upwards propagation of "throw".
//
// The thing is, if you don't do the following, you'd
// never know (programatically-speaking) WHY an error
// occurred in the top-level catch statement
function (req, res) {
try {
var msg = SomeService.signUpSync({
newUserProps: {}
});
try {
var whatever = SomeService.somethingElseSync({});
try {
return res.json({
message: msg
});
}//</try to build up some output, encode it to JSON, then respond>
catch (e) {
return res.serverError(e);
}
}//</try to do somethingElseSync>
catch (e){
return res.serverError(e);
}
}//</try to signupSync>
catch (e) {
return res.serverError(e);
}
}
// -------------------------------------------------------
// versus the following code whose statements are calls to asynchronous helpers:
// (notice how the structure is the same as above)
function (req, res) {
SomeService.signUp({
newUserProps: {}
}, function (err, msg) {
if (err) { return res.serverError(err); }
SomeService.somethingElse({}, function (err) {
if (err) { return res.serverError(err); }
// To be completely precise, we still use a try/catch
// for this one since building the dictionary and calling
// res.json is synchronous. Usually, you'd leave this
// try/catch out.
try {
return res.json({
message: msg
});
}//</try to build up some output, encode it to JSON, then respond>
catch (e) {
return res.serverError(e);
}
});//</try to doSomethingElse (async)>
});//</try to signup (async)>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment