Using returns and callbacks in the same function.
Sounds like crazy talk I know, but hear me out, I have good reason. I think.
Let's say you want to make several TCP servers in a node.js application. Have to listen on multiple ports or whatever. Using a factory function is your best bet to avoiding code repetition, right?
You end up with something like this:
var server = function (port, callback) {
var server = net.createServer(function (connection) {});
server.on("connection", function (socket) {
socket.on("data", // data stuff);
socket.on("error", function () {
console.log("error?", arguments);
socket.destroy();
});
socket.on("close", // cleanup stuff);
});
server.on("listening", callback);
server.listen(port);
};
Simple. Call a function, give it a port, get notified when server is ready to listen. Never be able to touch the server again.
Wait, that's not good. What if you want to reference the server later? To close it, for instance.
Curated JavaScript Essays
Get a series of curated essays on JavaScript. Lessons and insights from building software for production. No bullshit.
That's where using returns alongside callbacks comes in.
Just adding a return server
at the bottom of that function lets us do something like this:
var make_servers = function (callback) {
this.servers = {}
async.each(
[5000, 5001, 5002],
_.bind(function (port, callback) {
this.servers[port] = server(port, callback)
}, this),
function (err) {
callback()
}
)
return this
}
I sneakily added the async and underscore libraries because they make life easier.
make_servers
will generate three servers listening on ports 5000
to 5002
. The async library helped us ensure the main callback is only called once all the servers are ready to listen, and using _.bind
let us bind server generation to the current scope.
When this function is done it returns its scope, which now includes references to all the servers, and it will tell your code to keep going when all the servers are ready.
You'd use it something like this:
var stuff = make_servers(function () { // all servers listening });
// stuff.servers can access all servers
If you bind all the callbacks inside server
to current scope you can even keep track of connections. You'd end up with something like this:
var server = function (port) {
var server = net.createServer(function (connection) {});
server.on("connection", _.bind(function (socket) {
socket.id = shortid.generate();
this.connections[port][socket.id] = socket;
socket.on("data", // data stuff);
socket.on("error", function () {
console.log("error?", arguments);
socket.destroy();
});
socket.on("close", _.bind(function () {
delete this.connections[port][socket.id];
}, this));
}, this));
return function (callback) {
server.on("listening", callback);
server.listen(port);
return server;
};
};
Not much has changed. More things were bound to this
and the return value became a function because I feel partial application makes this code cleaner.
You'd call the server
factory like this now: this.servers[port] = server(port)(callback)
. The main benefit of this approach is that we can generate servers in a loop and activate them at a later time.
We've essentially decoupled server generation and server startup. Might come in handy.
The stuff
object from before is now going to have a stuff.connections
as well, which references all currently open connections to each port. Neat!
Another trick I sneaked into these examples is Javascript's powerful dynamic scoping. Getting semi-random functions to run in the same scope like that can really clean up your code.
At the expense of being almost too clever sometimes. Use at your own peril.
What do you think, is there a cleaner way to implement something like this?
Continue reading about My new favourite Javascript trick
Semantically similar articles hand-picked by GPT-4
- Go full-stack in 5min with your first cloud function
- JavaScript promises are just like monads and I can explain both in less than 2 minutes
- Async, await, catch – error handling that won't drive you crazy
- Modern backend is a JavaScript function
- How serverless beats servers
Want to become a JavaScript expert?
Learning from tutorials is great! You follow some steps, learn a smol lesson, and feel like you got this. Then you go into an interview, get a question from the boss, or encounter a new situation and o-oh.
Shit, how does this work again? 😅
That's the problem with tutorials. They're not how the world works. Real software is a mess. A best-effort pile of duct tape and chewing gum. You need deep understanding, not recipes.
Leave your email and get the JavaScript Essays series - a series of curated essays and experiments on modern JavaScript. Lessons learned from practice building production software.
Curated JavaScript Essays
Get a series of curated essays on JavaScript. Lessons and insights from building software for production. No bullshit.
Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.
Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.
Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help 👉 swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.
Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers 👉 ServerlessHandbook.dev
Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization
Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections
Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog
Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com
By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️