最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Node.js, express, and object instantiation insideoutside route endpoints - Stack Overflow

programmeradmin0浏览0评论

I have a question regarding object instantiation. Right now I have a fairly large application set up, with RESTful endpoints implemented on the back-end.

I was wondering what is the "best" way of object instantiation, and how the garbage collector is affected by what I do.

Take for example the following:

const MyClass = require('../controllers/myClassController');
const router = require('express').Router();

router.get('/', (req, res) => {
    console.log('GET request at api/someEndPoint');
    
    const myClass = new MyClass();
    myClass.doSomePromise()
        .then(() => {
            res.end();
        })
        .catch((err) => {
            res.status(500).end();
        });
});

Here, an object is instantiated INSIDE the route path implementation.

Now, takes this for example:

const MyClass = require('../controllers/myClassController');
const router = require('express').Router();

const myClass = new MyClass();

router.get('/', (req, res) => {
    console.log('GET request at api/someEndPoint');
    
    myClass.doSomePromise()
        .then(() => {
            res.end();
        })
        .catch((err) => {
            res.status(500).end();
        });
});

Here, the object is instantiated OUTSIDE the route path implementation. Seems to me as though this would persist indefinitely for the life of the application, whereas the first example, the variable myClass would get cleaned up.

So really I'm wondering:

  1. Am I thinking about this the right way?

  2. Is there a reason why I should do one method vs the other? It seems as though if I chose option 2, I might as well just make static methods on the class itself...

  3. What are some general pros/cons of each approach?

    I feel like in my head I'm fighting the battle that things aren't being correctly cleaned up, one way is better than the other in terms of how much traffic there is, race condition avoidance, etc!

I have a question regarding object instantiation. Right now I have a fairly large application set up, with RESTful endpoints implemented on the back-end.

I was wondering what is the "best" way of object instantiation, and how the garbage collector is affected by what I do.

Take for example the following:

const MyClass = require('../controllers/myClassController');
const router = require('express').Router();

router.get('/', (req, res) => {
    console.log('GET request at api/someEndPoint');
    
    const myClass = new MyClass();
    myClass.doSomePromise()
        .then(() => {
            res.end();
        })
        .catch((err) => {
            res.status(500).end();
        });
});

Here, an object is instantiated INSIDE the route path implementation.

Now, takes this for example:

const MyClass = require('../controllers/myClassController');
const router = require('express').Router();

const myClass = new MyClass();

router.get('/', (req, res) => {
    console.log('GET request at api/someEndPoint');
    
    myClass.doSomePromise()
        .then(() => {
            res.end();
        })
        .catch((err) => {
            res.status(500).end();
        });
});

Here, the object is instantiated OUTSIDE the route path implementation. Seems to me as though this would persist indefinitely for the life of the application, whereas the first example, the variable myClass would get cleaned up.

So really I'm wondering:

  1. Am I thinking about this the right way?

  2. Is there a reason why I should do one method vs the other? It seems as though if I chose option 2, I might as well just make static methods on the class itself...

  3. What are some general pros/cons of each approach?

    I feel like in my head I'm fighting the battle that things aren't being correctly cleaned up, one way is better than the other in terms of how much traffic there is, race condition avoidance, etc!

Share Improve this question edited Jun 20, 2020 at 9:12 CommunityBot 11 silver badge asked Dec 5, 2017 at 23:00 dvsoukupdvsoukup 1,60616 silver badges33 bronze badges 1
  • 1 If you don't need different instances in every request, there's no point in repeatedly constructing them and tearing them down. – Bergi Commented Dec 5, 2017 at 23:26
Add a ment  | 

2 Answers 2

Reset to default 9

It's all fairly simple really.

  1. If you only ever need one object for the duration of your entire application and you can use that one object for any request that happens to need to use it, then it's an "application cycle" type of object. Create one at initialization time, store its instance (probably as a module level variable) and use it from then on. As an example, if you had a some sort of cache object designed to be used by lots of request handlers, that would be something that you'd want to create once and then retain it and use the same object every time by all the request handlers that use it. As another example, some applications create one connection to their main database upon startup and then all other requests can use that one connection.

  2. If you need a fresh object for every single request that might use it, then create it inside the request handler and let normal garbage collection take care of cleaning it up when the request is done. For example if you need to create a Date() object in order to measure some time duration during a request, the simplest implementation will create a new Date() object within the request handler, use it for what you need to use it for and then let GC take care of it when the request is done.

  3. If you need an independent object for each request such that when multiple request handlers are "in-flight" at the same time, you need to make sure that each request handler has its own object and none are being shared or conflicting.

  4. There are also hybrid models (which are generally done this way as performance optimizations) where you might create a pool of objects and then each request that needs one can "check one out", use it for awhile and then return it to the pool when its done. This often saves time rebuilding objects from scratch when a small pool of objects can generally serve the needs of the server. Some databases use this "pool" model for database connections. Rather than create a new connection to the database for every single request that wants to do a query, the request might just obtain a database connection from the pool, make its query, then return the connection to the pool.

Internally in node.js, it uses a thread pool for some parts of the disk I/O implementation. Similar concept.


I was wondering what is the "best" way of object instantiation, and how the garbage collector is affected by what I do.

The best way is to pick the instantiation model that matches the usage. In option 1 above, the object will likely be stored for the entire application cycle and not GCed at all. In options 2 and 3, the objects will be created and GCed for each request. You shouldn't worry about creating and object for use and then letting it GC. That's how the language is designed to work for things that don't have durable lasting state and should be kept around longer.

Is there a reason why I should do one method vs the other? It seems as though if I chose option 2, I might as well just make static methods on the class itself...

If you have no request-specific state in the object and the state of the object itself does not get into trouble if there are multiple request handlers attempting to use the object at the same time, then you really just have a global service that any request handler who wants to use it can use it. It's up to you if you implement that as an object you create, store the instance of and then call traditional methods on that instance or implement it as more of a singleton that instantiates itself and you just call functions or static methods on it. That's just a coding style difference, not a functional difference.

What are some general pros/cons of each approach?

In general, you want as much encapsulation and local state as possible. No need to go putting things that are request-specific stuff into shared globals. Things being calculated or figured out for a specific request belong internal to the request-handler logic. So, just follow the four questions above to determine whether the object belongs local to a request handler or shared at a higher scope. If you can meet your goals by keeping things local, that's generally cleaner, safer and more flexible.

I feel like in my head I'm fighting the battle that things aren't being correctly cleaned up, one way is better than the other in terms of how much traffic there is, race condition avoidance, etc!

GC takes care of cleanup for you. You can rely on that. As long as you don't persistently store a reference to an object in a variable that has lasting scope, garbage collection will clean things up for you nicely. This is how the language is designed to be used. You should not be afraid of it. If, perhaps you came from a non-GC language like C or C++, you may be over thinking this. GC is there to be your friend. Very, very, very occasionally you may need to do some performance tuning that bees more aware of how much you're asking the GC to do, but that should pretty much never be something you worry about until you've actually found you have something that needs special optimization in that way.

The simplest way to avoid race conditions between request handlers is to not share any state between request handlers. If everything you need to handle a given request is created and used locally and never made available to any other request handler, then you will absolutely avoid any race or sharing conditions between multiple request handlers. In general, start there because that's the simplest and safest. Then, if you find there is something you need to do a performance optimization of, then you may explore some sharing or caching of certain types of objects and you will have to do that carefully in order to not introduce sharing problems. But, you'd rarely start your design trying to do that because most of the time that extra plexity is not needed.

  1. Yes, your thoughts are legitimate. Even though stuff like object instantion is probably a micro optimization.

  2. As Bergi already mentioned in the ments you shouldn't reconstruct or process anything again inside of your routes if you don't need to. You always need to keep in mind that the code inside of your route handler is executed on every single requests which reaches this route.

  3. So what you are literally asking is: Should I do const myClass = new MyClass(); once per application lifecycle or potentially up to thousands times a second. Only you could answer if you really need to create a new instance of your class every time a request for this route has to be processed. Probably you don't need to create an instance every time. So in order to avoid unnecessary work put it outside of your route handler.

As of #3 there is no real pro/con list for above reasons. If you can avoid the instanation inside of your route handler, then avoid it!

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论