The Node Knowledge Challenge — Part 3
This is in continuation of the famous GitHub repo node-beyond-basics. For the previous parts visit here Part 1, Part 2.
13. What’s the problem with the process uncaughtException
event? How is it different than the exit event?
The uncaughtException
event is emitted when an uncaught JavaScript exception bubbles all the way back to the event loop. By default, Node.js handles such exceptions by printing the stack trace to stderr and exiting with code 1, overriding any previously set process.exitCode
.
Adding a handler for the uncaughtException
event overrides this default behavior and the process will exit with code 0 which is clearly a problem as it signals to Node that the process is exiting without any problem. The standard advice about uncaughtException
is to avoid using it, but if you must do (say to report what happened or do cleanups), you should just let the process exit anyway:
process.on(‘uncaughtException’, (err) => {// something went unhandled.// Do any cleanup and exit anyway!console.error(err); // don’t do just that!// FORCE exit the process too.process.exit(1);});
The exit event is used to end the NodeJs
process, on the contrary uncaughtException
is used to catch an uncaught JavaScript
exception.
14. What are the 5 major steps that the require function does?
When Node invokes that require()
function with a local file path as the function’s only argument, Node
goes through the following five sequence of steps:
- Resolving: To find the absolute path of the file
- Loading: To determine the type of the file content
- Wrapping: To give the file its private scope. This is what makes both the
require
andmodule
objects local to every file we require - Evaluating: This is what the VM eventually does with the loaded code
- Caching: So that when we require this file again, we don’t go over all the steps another time.
15. How can you check for the existence of a local module?
We can use require.resolve
function, it will throw an error if the file doesn’t exist and will return the full path of the file once found.
function moduleAvailable(name) {try {require.resolve(name);return true;} catch(e){}return false;}if (moduleAvailable(‘module_mame’)) {// yeah we’ve got it!
16. What are circular modular dependencies in Node and how can they be avoided?
It’s better to understand circular modular dependencies using examples so let’s create two files module1.js and module2.js under lib/ and have them require each other:
lib/module1.js
exports.a = 1;require(“./module2”);exports.b = 2;exports.c = 3;
lib/module2.js
const Module1 = require(“./module1”);console.log(“Module1 is partially loaded here”, Module1);
When we execute module1.js
, we see the following
node lib/module1.jsModule1 is partially loaded here { a: 1 }
We required module2
before module1
was fully loaded and since module2
required module1
while it wasn’t fully loaded, what we get from the exports
object at that point is all the properties exported prior to the circular dependency. Only the a
property was reported because both b
and c
were exported after module2
required and printed module1
.
Node keeps this really simple. During the loading of a, module
, it builds the exports
object. You can require the module
before it’s done loading and you’ll get a partial exports object with whatever was defined so far.
A circular dependency is not necessarily a bad thing and may be useful in some cases. However, if possible, it is best to avoid it as it causes bugs that are hard to reason with. Below are the two ways to deal with the problem.
1. Code Refactoring:
Circular dependency is usually the result of improper code structure/design. Code refactoring is the best way to take care of the circular dependency issue.
2. Extend exports instead of re-assigning
If a circular dependency is inevitable and introducing an intermediary is not an option, we can consider changing the way we export methods from modules to accommodate for circular references.
17. What are the 3 file extensions that will be automatically tried by the require
function?
The 3 file extensions will be automatically tried by the require
function are as follows:
.js
.json
.node
We can actually see the support of the three extensions by looking at require.extensions
.
18. When creating an http
server and writing a response for a request, why is the end()
function required?
end()
is required as it will finishes sending the request and is a medium for Node
to know that request has been completed. If any parts of the body are unsent, it will flush them to the stream. If the request is chunked, this will send the terminating 0\r\n\r\n
.