Study up mother fucker
Node is a server side scripting language based on Google's V8 JavaScript engine. It is used to build scalable programs that are computationally simple but frequently accessed.
You can use node in I/O intensive web applications like video streaming site, real-time web applications, network applications, general-purpose applications, and distributed systems.
Node makes build scalable network programs easy. Advantages include:
- it is generally fast
- it almost never blocks
- offers unified programming language(javascript) and data type(json)
- everything is asynchronous
- yields great concurrency
- Node is single-threaded but highly scalable
- uses async, event-driven I/O instead of separate processes or threads
- able to achieve high output via single-threaded event loop and non-blockin I/O
4. What is the output and how else can the code below be writting in Node to produce the same output?
Input
console.log('first');
setTimeout(function() {
console.log('second');
}, 0);
console.log('third');
Log
first
third
second
Node Input
console.log('first');
setImmediately(function() {
console.log('second');
}, 0);
console.log('third');
setImmediately(fn) will be used in palce of setTimeout(fn, 0) since it is faster
Node is single threaded for async processing. By doing async processing on a single-thread under typical web loads, more performance and scalability can be achieved. single thread provide more gains than thread-based implementation
.
A callback function is called at the completion of a task. This allows other code to be run in the meantime and prevents blocking
. Being async, Node heavily relies on callbacks.
- handle every single error
- keep your code shallow
- modularize - split the callbacks into smaller, independent functions that can be called with some parameters then joining them
REPL performs read
, eval
, print
, and loop
tasks. REPL is used to execute ad-hoc javascript statements. The REPL shell allows entry to javascript and evaluates the results. REPL is criticle for the purpose of testing
, debugging
, and experimenting
.
- blocking functions: in a blocking operation, all other code is blocked form executing until an I/O event that is waited on occurs. Executes
synchronously
. - non-blocking functions: multiple I/O calls can be performed without the execution of the program being halted.
Streams are objects that allow reading of data from the source and writing of data to the destination as a continuous process
There are four types:
- read opeartion
- write operation
- read and write operations
- Duplex stream that performs computations based on the available input
- unused
- uncaught fatal exception
- fatal error
- non-function internal exception handler
- internal exception handler run-time failure
- internal javascript evaluation failure
- global: represents the global namespace and acts as a container for all other objects
- process: one of the global objects but can turn a synchronous function into an async callback. It can be accessed from anywhere in the code and it primarily gives back information about the application or the environment
- buffer: is a class in Node to handle binary data
It does not expose child threads and thread management methods to the developer. Technically, Node does spawn child threads for certain tasks such as async I/O, but these run behind the scenes and do not execute any application JavaScript code, nor block the main event loop.
If threading support is desired in a Node application, there are tools available to enable it, such as ChildProcess module
.
Unhandled exceptions in Node can be caught at the Process
level by attaching a handler for uncaughtException
event.
process.on('uncaughtException', function(err) {
console.log('Caught exception:', err);
});
However, uncaughtException
is a very crude mechanism for exception handling and may be removed from Node.js in the future.
If an exception has bubbled up all the way to the
Process
level, your application may be in an undefined state and would need to restart.
The preferred way is to add another layber between your application and the Node process which is called the domain.
Domains provide a way to handle multiple different I/O operations as a single group. So, by having your application, or part of it, running in a separate domain, you can safely handle exceptions at the domain level, before they reach the
Process
level.
15. How does Node support multi-processor platforms, and does it fully utilize all processor resources?
Since Node is by default a single-threaded application, it will run on a single processor core and will not take full advantage of multicore processors.
However, Node provides support for deployment on multi-core systems, to take greater advantage of the hardware.
The Cluster module is one of the core Node modules and it allows running multiple Node worker processes that will share the same port.
console.time("loop");
for (var i = 0; i < 1000000; i += 1){
// Do nothing
}
console.timeEnd("loop");
The time required to run this code in Google Chrome is considerably more than the time required to run it in Node. Explain why this is so, even though both use the v8 JavaScript Engine.
- within a web browser, declaring
i
outisde of a function's scope makes it global and therefore binds to thewindow
- as a result, running this requires the browser to repeatedly resolve
i
in a heavily populatedwindow
namespace - declaring any variable outside of any function's scope bindes it only to the module's own scope(not on the
window
), therefore faster to resolve
Task function and concurrency value
- first class functions
- function composition
- callback counters
- event loops
Stubs are functions/programs that simulate the behaviors of components/modules. Stubs provide canned answers to function calls made during test cases.
var fs = require('fs');
var writeFileStub = sinon.stub(fs, 'writeFile', function (path, data, cb) {
return cb(null);
});
expect(writeFileStub).to.be.called;
writeFileStub.restore();
A test pyramid describes the ratio of how many unit tests, integration tests and end-to-end test you should write.
An example for an HTTP API may look like this:
- lots of low-level unit tests for models (dependencies are stubbed)
- fewer integration tests, where you check how your models interact with each other (dependencies are not stubbed)
- less end-to-end tests, where you call your actual endpoints (dependencies are not stubbed)
There is no right answer for this. The goal here is to understand how deeply one knows the framework she/he uses. Tell what are the pros and cons of picking that framework.
Worker processes are extremely useful if you'd like to do data processing in the background, like sending out emails or processing images.
There are lots of options for this like RabbitMQ or Kafka.
XSS occurs when the attacker injects executable JavaScript code into the HTML response.
To mitigate these attacks, you have to set flags on the set-cookie HTTP header:
- HttpOnly - this attribute is used to help prevent attacks such as cross-site scripting since it does not allow the cookie to be accessed via JavaScript.
- secure - this attribute tells the browser to only send the cookie if the request is being sent over HTTPS.
When writing Node.js applications, ending up with hundreds or even thousands of dependencies can easily happen. For example, if you depend on Express, you depend on 27 other modules directly, and of course on those dependencies' as well, so manually checking all of them is not an option!
The only option is to automate the update / security audit of your dependencies. For that there are free and paid options:
npm outdated
- Trace by RisingStack
- NSP
- GreenKeeper
- Snyk
The event loop is in libuv
and not part of V8.
your code | your code | your code |
---|---|---|
V8 | core modules | core modules |
V8 | C++ Bindings | C++ Bindings |
V8 | libuv | c-ares, http, ... |
the OS | the OS | the OS |
- the entity that handles external events and converts them to callback invocations
- a loop that picks events from the event queue and pushes their callback to the callstack
- decides what goes where.. lives between V8(heap/stack) and queue
If there is nothing to do, Node will exit - process.exit()
.
- http-parser
- c-ares
- OpenSSL
- zlib
They have their own source code and license.
Yes it can, with Chakra!
Because 'exports' is just a reference(alias) to 'module.exports', and when you change exports, you change that reference and you are no longer changing the API. You are changing some local variable.
Every node file gets its own IIFE behind the scenes. Node wraps your code in a function.
console.log(arguments)
will give you arguments when ran with Node
29. The objects: exports, require, and module are globally available in every file but they are different in every file. How?
Because of that magic IIFE...
> require('module').wrapper
[ '(function (exports, require, module, __filename, __dirname) {
// ...
});' ]
- attempt to resolve what you are attempting to require. it will try to map that string that you required into an actual path on the file system this path could be local to your node, node_modules, parent node_modules, etc..
- loads content of that file after it resolves it into memory
- wraps the content with that magic IIFE
- evaluates the file, probably with V8
- cache - the next time you require the exact same file, the last steps will not need to happen. Node will just read it from the cache
Using require.resolve('module-name')
will check to see if a module exists and throws and error that you can catch.
Node allows this and you will NOT get an error. The only problem is that when one module is still loading, the other module will get a partial version of that loading module.
module1: require('module2');
module2: require('module1');
It will try .js
, then, .json
, then .node
.
- If you have a
.json
file, you just require it directly, and Node will automatically parse it and put it in a JavaScript object for you - A
.node
file is a binary file. So if you have some C code, you can just compile the C code into a.node
file using some node package and you can just require the C code directly. You don't have to require the JavaScript
Node can tell the OS to run a command and the OS will give Node back some output. But spawn and exec both allow you to do that.
Differences and significance:
spawn | exec |
---|---|
no shell (security risk) | uses shell |
streams (for large data) | buffer output |
What is spawn(), execFile(), fork(), exec()?
The argumen is interesting because it is a V8 argument. Node relays that to V8 and V8 will start in strict mode.
$ node --use-strict
> a = 1
ReferenceError: a is not defined
When you specify arguments to the node command those arguments get captured in an array and all the argument values are captured as strings.
$ node -p process.argv 42 hello world
> [ '/usr/local/kev/node/8.9.6/bin/node', '42', 'hello', 'world' ]
37. How can we do one final operation before a Node process crashes? Can that operation be done asynchoronously?
Yes! uncaughtException
is a generic error - "I'm crashing, what do you want me to do?".
This operation cannot be done async because at this point, the event loop just gave up. Its not working anymore!
process.on('uncaughtException', (err) => {
fs.writeSync(1, `Caught exception: ${err}\n`);
process.exit(1); // not optional, let process exit!
});
Terrible mistake happend, let Node restart.
Run .help
in the terminal.
Gets last executed value and captures that in the underscore variable.
$ node
>
> Math.random()
0.345234523452345
>
> const a = _
>
> a
0.345234523452345
>
Buffer.alloc
gives you a chunk of the memory and actually resets that chunk for you with zeros
Buffer.allocUnsafe
just gives you a chunk of the memory and you can read what's already in that memory, so you can actually leak information. It us faster than Buffer.alloc
The cluster module helps with scalabilty, particularly with cloning. Good for scaling one server on multiple cores. Very simple process.
But... the workers have their own memory, V8, callstack. No longer share things in memory, no longer cache things in memory. If you want to cache, you gotta build another entity with its own API and have all the workers can work with that entity.
PM2 uses cluster module by default. If using PM2 on a multicore processor and caching, you're prob doing it wrong. Some workers will not be aware of other worker's memory.
Depends on the situation. Ex: if you initializing the server, sync is okay because it's a one time thing and everything else is depending on all of it to be loaded.
> console.dir(global, { depth: 0 })
You can't! Instead use node --inspect
. Node will give you a link that you can go to and it'll be as if you're using Chrome dev-tools
Callback !== Asynchrony
You want your functions to always be synchronous or always async
46. What is the difference between using event emitters and using callback functions for async handling of code?
event emitter: objects that you can extend and build in custom logic. event emits can just omit those dynamic strings - any string - and you can register handlers to be invoked when those strings get emitted.
callbacks: are like a special form of event emitter that you can register to once. if you want multiple callbacks to be fired for the same event, you want to use an event emitter.
Event emitters are very important becausae they are at the core of Node. Mostly all other objects in node implement event emitters.
Understanding Node Event-Driven Archi tecture
Examples are transform streams: crypto.createCipher
and zlib.createGzip
. Streams are the next important thing in Node. If youre not using streams, you're doing it wrong!
Sockets are duplex streams.
Allows you to not use memory when you don't need to. Working with big data doesn't require allocating a lot of memory in node, you just use stings
Node.js Streams: Everything you need to know
Streams are...
- collections of data that might not be available all at once and don't fit in memory
- like arrays but special types of arrays (arrays that you don't have but you only get once piece at a time)
- don't have to fit in memory because you might not want to handle all of the data at once
Gzip can compress a 10gb file to ~40mb.
Important:
readable.pipe(writable)
.
a.pipe(b).pipe(c).pipe(d);
B and C have to be both readable and writable. A can just be readable and D can just be writable.