admin管理员组文章数量:1134246
I recently came across this article on how to write a singleton in Node.js. I know the documentation of require
states that:
Modules are cached after the first time they are loaded. Multiple calls to
require('foo')
may not cause the module code to be executed multiple times.
So it seems that every required module can be easily used as a singleton without the singleton boilerplate-code.
Question:
Does the above article provide a round about solution to creating a singleton?
I recently came across this article on how to write a singleton in Node.js. I know the documentation of require
states that:
Modules are cached after the first time they are loaded. Multiple calls to
require('foo')
may not cause the module code to be executed multiple times.
So it seems that every required module can be easily used as a singleton without the singleton boilerplate-code.
Question:
Does the above article provide a round about solution to creating a singleton?
Share Improve this question edited Aug 23, 2018 at 12:51 r0- 2,4881 gold badge25 silver badges29 bronze badges asked Nov 1, 2012 at 14:23 mkoryakmkoryak 57.9k64 gold badges203 silver badges262 bronze badges 1- 3 Here is a 5 min. explanation on this topic (written after v6 and npm3): medium.com/@lazlojuly/… – lazlojuly Commented Sep 6, 2016 at 8:19
10 Answers
Reset to default 155All of the above is overcomplicated. There is a school of thought which says design patterns are showing deficiencies of actual language.
Languages with prototype-based OOP (classless) do not need a singleton pattern at all. You simply create a single(ton) object on the fly and then use it.
As for modules in node, yes, by default they are cached, but it can be tweaked for example if you want hot-loading of module changes.
But yes, if you want to use shared object all over, putting it in a module exports is fine. Just do not complicate it with "singleton pattern", no need for it in JavaScript.
This has basically to do with nodejs caching. Plain and simple.
https://nodejs.org/api/modules.html#modules_caching
(v 6.3.1)
Caching
Modules are cached after the first time they are loaded. This means (among other things) that every call to require('foo') will get exactly the same object returned, if it would resolve to the same file.
Multiple calls to require('foo') may not cause the module code to be executed multiple times. This is an important feature. With it, "partially done" objects can be returned, thus allowing transitive dependencies to be loaded even when they would cause cycles.
If you want to have a module execute code multiple times, then export a function, and call that function.
Module Caching Caveats
Modules are cached based on their resolved filename. Since modules may resolve to a different filename based on the location of the calling module (loading from node_modules folders), it is not a guarantee that require('foo') will always return the exact same object, if it would resolve to different files.
Additionally, on case-insensitive file systems or operating systems, different resolved filenames can point to the same file, but the cache will still treat them as different modules and will reload the file multiple times. For example, require('./foo') and require('./FOO') return two different objects, irrespective of whether or not ./foo and ./FOO are the same file.
So in simple terms.
If you want a Singleton; export an object.
If you do not want a Singleton; export a function (and do stuff/return stuff/whatever in that function).
To be VERY clear, if you do this properly it should work, look at https://stackoverflow.com/a/33746703/1137669 (Allen Luce's answer). It explains in code what happens when caching fails due to differently resolved filenames. But if you ALWAYS resolve to the same filename it should work.
Update 2016
creating a true singleton in node.js with es6 symbols Another solution: in this link
Update 2020
This answer refers to CommonJS (Node.js's own way to import/export modules). Node.js will most likely be switching over to ECMAScript Modules: https://nodejs.org/api/esm.html (ECMAScript is the real name of JavaScript if you didn't know)
When migrating to ECMAScript read the following for now: https://nodejs.org/api/esm.html#esm_writing_dual_packages_while_avoiding_or_minimizing_hazards
No. When Node's module caching fails, that singleton pattern fails. I modified the example to run meaningfully on OSX:
var sg = require("./singleton.js");
var sg2 = require("./singleton.js");
sg.add(1, "test");
sg2.add(2, "test2");
console.log(sg.getSocketList(), sg2.getSocketList());
This gives the output the author anticipated:
{ '1': 'test', '2': 'test2' } { '1': 'test', '2': 'test2' }
But a small modification defeats caching. On OSX, do this:
var sg = require("./singleton.js");
var sg2 = require("./SINGLETON.js");
sg.add(1, "test");
sg2.add(2, "test2");
console.log(sg.getSocketList(), sg2.getSocketList());
Or, on Linux:
% ln singleton.js singleton2.js
Then change the sg2
require line to:
var sg2 = require("./singleton2.js");
And bam, the singleton is defeated:
{ '1': 'test' } { '2': 'test2' }
I don't know of an acceptable way to get around this. If you really feel the need to make something singleton-like and are okay with polluting the global namespace (and the many problems that can result), you can change the author's getInstance()
and exports
lines to:
singleton.getInstance = function(){
if(global.singleton_instance === undefined)
global.singleton_instance = new singleton();
return global.singleton_instance;
}
module.exports = singleton.getInstance();
That said, I've never run into a situation on a production system where I needed to do anything like this. I've also never felt the need to use the singleton pattern in Javascript.
Looking a little further at the Module Caching Caveats in the Modules docs:
Modules are cached based on their resolved filename. Since modules may resolve to a different filename based on the location of the calling module (loading from node_modules folders), it is not a guarantee that require('foo') will always return the exact same object, if it would resolve to different files.
So, depending on where you are when you're requiring a module, it's possible to get a different instance of the module.
Sounds like modules are not a simple solution to creating singletons.
Edit: Or maybe they are. Like @mkoryak, I can't come up with a case where a single file might resolve to different filenames (without using symlinks). But (as @JohnnyHK comments), multiple copies of a file in different node_modules
directories will each be loaded and stored separately.
A singleton in node.js (or in browser JS, for that matter) like that is completely unnecessary.
Since modules are cached and stateful, the example given on the link you provided could easily be rewritten much more simply:
var socketList = {};
exports.add = function (userId, socket) {
if (!socketList[userId]) {
socketList[userId] = socket;
}
};
exports.remove = function (userId) {
delete socketList[userId];
};
exports.getSocketList = function () {
return socketList;
};
// or
// exports.socketList = socketList
The only answer here that uses ES6 classes
// SummaryModule.js
class Summary {
init(summary) {
this.summary = summary
}
anotherMethod() {
// do something
}
}
module.exports = new Summary()
require this singleton with:
const summary = require('./SummaryModule')
summary.init(true)
summary.anotherMethod()
Only problem here is that you cannot pass params to the class constructor but that can be circumvented by manually calling an init
method.
You don't need anything special to do a singleton in js, the code in the article could just as well be:
var socketList = {};
module.exports = {
add: function() {
},
...
};
Outside node.js (for instance, in browser js), you need to add the wrapper function manually (it is done automatically in node.js):
var singleton = function() {
var socketList = {};
return {
add: function() {},
...
};
}();
Singletons are fine in JS, they just don't need to be so verbose.
In node if you need a singleton, for instance to use the same ORM/DB instance across various files in your server layer, you can stuff the reference into a global variable.
Just write a module that creates the global var if it doesn't exist, then returns a reference to that.
@allen-luce had it right with his footnote code example copied here:
singleton.getInstance = function(){
if(global.singleton_instance === undefined)
global.singleton_instance = new singleton();
return global.singleton_instance;
};
module.exports = singleton.getInstance();
but it is important to note that using the new
keyword is not required. Any old object, function, iife, etc. will work - there is no OOP voodoo happening here.
bonus points if you closure a some obj inside a function that returns a reference to it, and make that function a global - then even reassignment of the global variable won't clobber the instances already created from it - though this is questionably useful.
Keeping it simple.
foo.js
function foo() {
bar: {
doSomething: function(arg, callback) {
return callback('Echo ' + arg);
};
}
return bar;
};
module.exports = foo();
Then just
var foo = require(__dirname + 'foo');
foo.doSomething('Hello', function(result){ console.log(result); });
If you want to use classes, it's the shortest and most beautiful
module.exports = new class foo {...}
本文标签: javascriptSingleton pattern in nodejsis it neededStack Overflow
版权声明:本文标题:javascript - Singleton pattern in nodejs - is it needed? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736776313a1952359.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论