PROFESSIONALLY OPTIMIZED WEBSITES STARTING AT $995
Our team of SEO Web Design gurus are standing by to assist you achieve your online marketing goals.

(541) 799-0040‬

info@seowebdesignllc.com

REQUEST QUOTE
SEO Web Design, LLC aims to improve business by delivering effective solutions based on innovative technologies and professional designs. Discover the variety of services we offer and convince yourself on the basis of the latest works that we've done. We love building fresh, unique and usable websites optimized specifically for your niche.

Responsive Web Design

SEO / SEM / Social Media

Conversion Rate Optimization

Email Marketing

Online Presence Analysis

Web Hosting

(541) 799-0040‬

Made in the USA

0
  • No products in the cart.
mobile-logo
Top
SEO Web Design /  Web Design  / Exploring Node.js Internals

Exploring Node.js Internals

Node.js is an interesting tool for web developers. With its high level of concurrency, it has become a leading candidate for people choosing tools to use in web development. In this article, we will learn about what makes up Node.js, give it a meaningful definition, understand how the internals of Node.js interact with one another, and explore the project repository for Node.js on GitHub.

Since the introduction of Node.js by Ryan Dahl [1] at the European JSConf on 8 November 2009, it has seen wide usage across the tech industry. Companies such as Netflix, Uber, and LinkedIn give credibility to the claim that Node.js can withstand a high amount of traffic and concurrency.

Armed with basic knowledge, beginner and intermediate developers of Node.js struggle with many things: “It’s just a runtime!” “It has event loops!” “Node.js is single-threaded like JavaScript!”

While some of these claims are true, we will dig deeper into the Node.js runtime, understanding how it runs JavaScript, seeing whether it actually is single-threaded, and, finally, better understanding the interconnection between its core dependencies, V8 and libuv.

Prerequisites

  • Basic knowledge of JavaScript
  • Familiarity with Node.js semantics (require, fs)

What Is Node.js?

It might be tempting to assume what many people have believed about Node.js, the most common definition of it being that it’s a runtime for the JavaScript language. To consider this, we should understand what led to this conclusion.

Node.js is often described as a combination of C++ and JavaScript. The C++ part consists of bindings running low-level code that make it possible to access hardware connected to the computer. The JavaScript part takes JavaScript as its source code and runs it in a popular interpreter of the language, named the V8 engine.

With this understanding, we could describe Node.js as a unique tool that combines JavaScript and C++ to run programs outside of the browser environment.

But could we actually call it a runtime? To determine that, let’s define what a runtime is.

What is a runtime? https://t.co/eaF4CoWecX [2]

— Christian Nwamba (@codebeast) March 5, 2020 [3]

In one of his answers [4] on StackOverflow, DJNA [5] defines a runtime environment as “everything you need to execute a program, but no tools to change it”. According to this definition, we can confidently say that everything that is happening while we run our code (in any language whatsoever) is running in a runtime environment.

Other languages have their own runtime environment. For Java, it is the Java Runtime Environment (JRE). For .NET, it is the Common Language Runtime (CLR). For Erlang, it is BEAM.

Nevertheless, some of these runtimes have other languages that depend on them. For example, Java has Kotlin, a programming language that compiles to code that a JRE can understand. Erlang has Elixir. And we know there are many variants for .NET development, which all run in the CLR, known as the .NET Framework.

Now we understand that a runtime is an environment provided for a program to be able to execute successfully, and we know that V8 and a host of C++ libraries make it possible for a Node.js application to execute. Node.js itself is the actual runtime that binds everything together to make those libraries an entity, and it understands just one language — JavaScript — regardless of what Node.js is built with.

Internal Structure Of Node.js

When we attempt to run a Node.js program (such as index.js) from our command line using the command node index.js, we are calling the Node.js runtime. This runtime, as mentioned, consists of two independent dependencies, V8 and libuv.

Core Node.js dependencies
Core Node.js Dependencies ( Large preview [6])

V8 is a project created and maintained by Google. It takes JavaScript source code and runs it outside of the browser environment. When we run a program through a node command, the source code is passed by the Node.js runtime to V8 for execution.

The libuv library contains C++ code that enables low-level access to the operating system. Functionality such as networking, writing to the file system, and concurrency are not shipped by default in V8, which is the part of Node.js that runs our JavaScript code. With its set of libraries, libuv provides these utilities and more in a Node.js environment.

Node.js is the glue that holds the two libraries together, thereby becoming a unique solution. Throughout the execution of a script, Node.js understands which project to pass control to and when.

Interesting APIs For Server-Side Programs

If we study a little history of JavaScript, we would know that it’s meant to add some functionality and interaction to a page in the browser. And in the browser, we would interact with the elements of the document object model (DOM) that make up the page. For this, a set of APIs exists, referred to collectively as the DOM API.

The DOM exists only in the browser; it is what is parsed to render a page, and it is basically written in the markup language known as HTML. Also, the browser exists in a window, hence the window object, which acts as a root for all of the objects on the page in a JavaScript context. This environment is called the browser environment, and it is a runtime environment for JavaScript.

Node.js APIs call libuv for some functions
Node.js APIs interact with libuv ( Large preview [7])

In a Node.js environment, we have nothing like a page, nor a browser — this nullifies our knowledge of the global window object [8]. What we do have is a set of APIs that interact with the operating system to provide additional functionality to a JavaScript program. These APIs for Node.js (fs, path, buffer, events, HTTP, and so on), as we have them, exist only for Node.js, and they are provided by Node.js (itself a runtime) so that we can run programs written for Node.js.

Experiment: How fs.writeFile Creates A New File

If V8 was created to run JavaScript outside of the browser, and if a Node.js environment does not have the same context or environment as a browser, then how would we do something like access the file system or make an HTTP server?

As an example, let’s take a simple Node.js application that writes a file to the file system in the current directory:

const fs = require("fs")

fs.writeFile("./test.txt", "text");

As shown, we are trying to write a new file to the file system. This feature is not available in the JavaScript language; it is available only in a Node.js environment. How does this get executed?

To understand this, let’s take a tour of the Node.js code base.

Heading over to the GitHub repository for Node.js [9], we see two main folders, src and lib. The lib folder has the JavaScript code that provides the nice set of modules that are included by default with every Node.js installation. The src folder contains the C++ libraries for libuv.

If we look in the src folder and go through the fs.js file [10], we will see that it is full of impressive JavaScript code. On line 1880 [11], we will notice an exports statement. This statement exports everything we can access by importing the fs module, and we can see that it exports a function named writeFile.

Searching for function writeFile( (where the function is defined) leads us to line 1303 [12], where we see that the function is defined with four parameters:

function writeFile(path, data, options, callback) {
  callback = maybeCallback(callback || options);
  options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
  const flag = options.flag || 'w';

  if (!isArrayBufferView(data)) {
    validateStringAfterArrayBufferView(data, 'data');
    data = Buffer.from(data, options.encoding || 'utf8');
  }

  if (isFd(path)) {
    const isUserFd = true;
    writeAll(path, isUserFd, data, 0, data.byteLength, callback);
    return;
  }

  fs.open(path, flag, options.mode, (openErr, fd) => {
    if (openErr) {
      callback(openErr);
    } else {
      const isUserFd = false;
      writeAll(fd, isUserFd, data, 0, data.byteLength, callback);
    }
  });
}

On lines 1315 [13] and 1324 [14], we see that a single function, writeAll, is called after some validation checks. We find this function on line 1278 [15] in the same fs.js file [16].

function writeAll(fd, isUserFd, buffer, offset, length, callback) {
  // write(fd, buffer, offset, length, position, callback)
  fs.write(fd, buffer, offset, length, null, (writeErr, written) => {
    if (writeErr) {
      if (isUserFd) {
        callback(writeErr);
      } else {
        fs.close(fd, function close() {
          callback(writeErr);
        });
      }
    } else if (written === length) {
      if (isUserFd) {
        callback(null);
      } else {
        fs.close(fd, callback);
      }
    } else {
      offset += written;
      length -= written;
      writeAll(fd, isUserFd, buffer, offset, length, callback);
    }
  });
}

It is also interesting to note that this module is attempting to call itself. We see this on line 1280 [17], where it is calling fs.write. Looking for the write function, we will discover a little information.

The write function starts on line 571 [18], and it runs about 42 lines [19]. We see a recurring pattern in this function: the way it calls a function on the binding module, as seen on lines 594 [20] and 612 [21]. A function on the binding module is called not only in this function, but in virtually any function that is exported in the fs.js file [22] file. Something must be very special about it.

The binding variable is declared on line 58 [23], at the very top of the file, and a click on that function call reveals some information, with the help of GitHub.

Declaration of the binding variable
Declaration of the binding variable ( Large preview [24])

This internalBinding function is found in the module named loaders [25]. The main function of the loaders module is to load all libuv libraries and connect them through the V8 project with Node.js. How it does this is rather magical, but to learn more we can look closely at the writeBuffer function that is called by the fs module.

We should look where this connects with libuv, and where V8 comes in. At the top of the loaders module, some good documentation there states this:

// This file is compiled and run by node.cc before bootstrap/node.js
// was called, therefore the loaders are bootstraped before we start to
// actually bootstrap Node.js. It creates the following objects:
//
// C++ binding loaders:
// - process.binding(): the legacy C++ binding loader, accessible from user land
//   because it is an object attached to the global process object.
//   These C++ bindings are created using NODE_BUILTIN_MODULE_CONTEXT_AWARE()
//   and have their nm_flags set to NM_F_BUILTIN. We do not make any guarantees
//   about the stability of these bindings, but still have to take care of
//   compatibility issues caused by them from time to time.
// - process._linkedBinding(): intended to be used by embedders to add
//   additional C++ bindings in their applications. These C++ bindings
//   can be created using NODE_MODULE_CONTEXT_AWARE_CPP() with the flag
//   NM_F_LINKED.
// - internalBinding(): the private internal C++ binding loader, inaccessible
//   from user land unless through `require('internal/test/binding')`.
//   These C++ bindings are created using NODE_MODULE_CONTEXT_AWARE_INTERNAL()
//   and have their nm_flags set to NM_F_INTERNAL.
//
// Internal JavaScript module loader:
// - NativeModule: a minimal module system used to load the JavaScript core
//   modules found in lib/**/*.js and deps/**/*.js. All core modules are
//   compiled into the node binary via node_javascript.cc generated by js2c.py,
//   so they can be loaded faster without the cost of I/O. This class makes the
//   lib/internal/*, deps/internal/* modules and internalBinding() available by
//   default to core modules, and lets the core modules require itself via
//   require('internal/bootstrap/loaders') even when this file is not written in
//   CommonJS style.

What we learn here is that for every module called from the binding object in the JavaScript section of the Node.js project, there is an equivalent of it in the C++ section, in the src folder.

From our fs tour, we see that the module that does this is located in node_file.cc [26]. Every function that is accessible through the module is defined in the file; for example, we have the writeBuffer on line 2258 [27]. The actual definition of that method in the C++ file is on line 1785 [28]. Also, the call to the part of libuv that does the actual writing to the file can be found on lines 1809 [29] and 1815 [30], where the libuv function uv_fs_write is called asynchronously.

What Do We Gain From This Understanding?

Just like many other interpreted language runtimes, the runtime of Node.js can be hacked. With greater understanding, we could do things that are impossible with the standard distribution just by looking through the source. We could add libraries to make changes to the way some functions are called. But above all, this understanding is a foundation for further exploration.

Is Node.js Single-Threaded?

Sitting on libuv and V8, Node.js has access to some additional functionalities that a typical JavaScript engine running in the browser does not have.

Any JavaScript that runs in a browser will execute in a single thread. A thread in a program’s execution is just like a black box sitting on top of the CPU in which the program is being executed. In a Node.js context, some code could be executed in as many threads as our machines can carry.

To verify this particular claim, let’s explore a simple code snippet.

const fs = require("fs");
// A little benchmarking
const startTime = Date.now()
fs.writeFile("./test.txt", "test", (err) => {
    If (error) {
        console.log(err)
    }
    console.log("1 Done: ", Date.now() — startTime)
});

In the snippet above, we are trying to create a new file on the disk in the current directory. To see how long this could take, we’ve added a little benchmark to monitor the start time of the script, which gives us the duration in milliseconds of the script that is creating the file.

If we run the code above, we will get a result like this:

Result of the time it takes to create a single file in Node.js
Time taken to create a single file in Node.js ( Large preview [31])
$ node ./test.js
    -> 1 Done: 0.003s

This is very impressive: just 0.003 seconds.

But let’s do something really interesting. First let’s duplicate the code that generates the new file, and update the number in the log statement to reflect their positions:

const fs = require("fs");
// A little benchmarking
const startTime = Date.now()
fs.writeFile("./test1.txt", "test", function (err) {
     if (err) {
        console.log(err)
    }
    console.log("1 Done: %ss", (Date.now() — startTime) / 1000)
});

fs.writeFile("./test2.txt", "test", function (err) {
     if (err) {
        console.log(err)
    }
    console.log("2 Done: %ss", (Date.now() — startTime) / 1000)
});


fs.writeFile("./test3.txt", "test", function (err) {
     if (err) {
        console.log(err)
    }
    console.log("3 Done: %ss", (Date.now() — startTime) / 1000)
});

fs.writeFile("./test4.txt", "test", function (err) {
     if (err) {
        console.log(err)
    }
    console.log("4 Done: %ss", (Date.now() — startTime) / 1000)
});

If we attempt to run this code, we will get something that blows our minds. Here is my result:

Result of the time it takes to create multiple files
Creating many files at once ( Large preview [32])

First, we will notice that the results are not consistent. Secondly, we see that the time has increased. What’s happening?

Low-Level Tasks Get Delegated

Node.js is single-threaded, as we know now. Parts of Node.js are written in JavaScript, and others in C++. Node.js uses the same concepts of the event loop and the call stack that we are familiar with from the browser environment, meaning that the JavaScript parts of Node.js are single-threaded. But the low-level task that requires speaking with an operating system is not single-threaded.

Low-level tasks are delegated to the OS through libuv
Node.js low-level task delegation ( Large preview [33])

When a call is recognized by Node.js as being intended for libuv, it delegates this task to libuv. In its operation, libuv requires threads for some of its libraries, hence the use of the thread pool in executing Node.js programs when they are needed.

By default, the Node.js thread pool provided by libuv has four threads in it. We could increase or reduce this thread pool by calling process.env.UV_THREADPOOL_SIZE at the top of our script.

// script.js
process.env.UV_THREADPOOL_SIZE = 6;

// …
// …

What Happens With Our File-Making Program

It appears that once we invoke the code to create our file, Node.js hits the libuv part of its code, which dedicates a thread for this task. This section in libuv gets some statistical information about the disk before working on the file.

This statistical checking could take a while to complete; hence, the thread is released for some other tasks until the statistical check is completed. When the check is completed, the libuv section occupies any available thread or waits until a thread becomes available for it.

We have only four calls and four threads, so there are enough threads to go around. The only question is how fast each thread will process its task. We will notice that the first code to make it into the thread pool will return its result first, and it blocks all of the other threads while running its code.

Conclusion

We now understand what Node.js is. We know it’s a runtime. We’ve defined what a runtime is. And we’ve dug deep into what makes up the runtime provided by Node.js.

We have come a long way. And from our little tour of the Node.js repository on GitHub [34], we can explore any API we might be interested in, following the same process we took here. Node.js is open source, so surely we can dive into the source, can’t we?

Even though we have touched on several of the low levels of what happens in the Node.js runtime, we mustn’t assume that we know it all. The resources below point to some information on which we can build our knowledge:

  • Introduction to Node.js [35]
    Being an official website, Node.dev explains what Node.js is, as well as its package managers, and lists web frameworks built on top of it.
  • “ JavaScript & Node.js [36]”, The Node Beginner Book
    This book by Manuel Kiessling [37] does a fantastic job of explaining Node.js, after warning that JavaScript in the browser is not the same as the one in Node.js, even though both are written in the same language.
  • Beginning Node.js [38]
    This beginner book goes beyond an explanation of the runtime. It teaches about packages and streams and creating a web server with the Express framework.
  • LibUV [39]
    This is the official documentation of the supporting C++ code of the Node.js runtime.
  • V8
    This is the official documentation of the JavaScript engine that makes it possible to write Node.js with JavaScript.
Smashing Editorial(ra, il, al)

References

  1. ^ Ryan Dahl (twitter.com)
  2. ^ https://t.co/eaF4CoWecX (t.co)
  3. ^ March 5, 2020 (twitter.com)
  4. ^ his answers (stackoverflow.com)
  5. ^ DJNA (stackoverflow.com)
  6. ^ Large preview (cloud.netlifyusercontent.com)
  7. ^ Large preview (cloud.netlifyusercontent.com)
  8. ^ window object (developer.mozilla.org)
  9. ^ repository for Node.js (github.com)
  10. ^ fs.js file (github.com)
  11. ^ line 1880 (github.com)
  12. ^ line 1303 (github.com)
  13. ^ 1315 (github.com)
  14. ^ 1324 (github.com)
  15. ^ line 1278 (github.com)
  16. ^ fs.js file (github.com)
  17. ^ line 1280 (github.com)
  18. ^ line 571 (github.com)
  19. ^ 42 lines (github.com)
  20. ^ 594 (github.com)
  21. ^ 612 (github.com)
  22. ^ fs.js file (github.com)
  23. ^ line 58 (github.com)
  24. ^ Large preview (cloud.netlifyusercontent.com)
  25. ^ loaders (github.com)
  26. ^ in node_file.cc (github.com)
  27. ^ line 2258 (github.com)
  28. ^ line 1785 (github.com)
  29. ^ 1809 (github.com)
  30. ^ 1815 (github.com)
  31. ^ Large preview (cloud.netlifyusercontent.com)
  32. ^ Large preview (cloud.netlifyusercontent.com)
  33. ^ Large preview (cloud.netlifyusercontent.com)
  34. ^ Node.js repository on GitHub (github.com)
  35. ^ Introduction to Node.js (nodejs.dev)
  36. ^ JavaScript & Node.js (www.nodebeginner.org)
  37. ^ Manuel Kiessling (twitter.com)
  38. ^ Beginning Node.js (www.amazon.com)
  39. ^ LibUV (docs.libuv.org)

Powered by WPeMatico

developers smashing magazine web designers
Share

smashmag@seowdllc.com

Smashing Magazine is a website and eBook publisher that offers editorial content and professional resources for web developers and web designers.

Responsive Web And Desktop Development With Flutter
Previous
17 Tools for Effective Customer Engagement
Next

代做工资流水公司保定日常消费流水制作西安银行流水电子版办理厦门办入职银行流水三亚房贷工资流水 查询徐州打流水账单石家庄房贷流水代做岳阳自存流水代办惠州打银行流水单银川银行流水单代办嘉兴打工资流水单常州代办房贷银行流水许昌工资代付流水报价昆明签证银行流水 公司泉州对公银行流水打印湛江车贷工资流水 多少钱绵阳银行流水电子版价格鞍山转账银行流水查询莆田工资流水账单费用杭州代开消费贷流水湘潭办流水单常州工资流水代做常德日常消费流水模板滁州工资证明样本南通背调流水费用南京工资代付流水模板信阳背调银行流水多少钱潮州房贷银行流水 代办宜昌企业对公流水代开衡阳制作个人流水东莞办消费贷流水香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声卫健委通报少年有偿捐血浆16次猝死汪小菲曝离婚始末何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言男子被猫抓伤后确诊“猫抓病”周杰伦一审败诉网易中国拥有亿元资产的家庭达13.3万户315晚会后胖东来又人满为患了高校汽车撞人致3死16伤 司机系学生张家界的山上“长”满了韩国人?张立群任西安交通大学校长手机成瘾是影响睡眠质量重要因素网友洛杉矶偶遇贾玲“重生之我在北大当嫡校长”单亲妈妈陷入热恋 14岁儿子报警倪萍分享减重40斤方法杨倩无缘巴黎奥运考生莫言也上北大硕士复试名单了许家印被限制高消费奥巴马现身唐宁街 黑色着装引猜测专访95后高颜值猪保姆男孩8年未见母亲被告知被遗忘七年后宇文玥被薅头发捞上岸郑州一火锅店爆改成麻辣烫店西双版纳热带植物园回应蜉蝣大爆发沉迷短剧的人就像掉进了杀猪盘当地回应沈阳致3死车祸车主疑毒驾开除党籍5年后 原水城县长再被查凯特王妃现身!外出购物视频曝光初中生遭15人围殴自卫刺伤3人判无罪事业单位女子向同事水杯投不明物质男子被流浪猫绊倒 投喂者赔24万外国人感慨凌晨的中国很安全路边卖淀粉肠阿姨主动出示声明书胖东来员工每周单休无小长假王树国卸任西安交大校长 师生送别小米汽车超级工厂正式揭幕黑马情侣提车了妈妈回应孩子在校撞护栏坠楼校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变老人退休金被冒领16年 金额超20万西藏招商引资投资者子女可当地高考特朗普无法缴纳4.54亿美元罚金浙江一高校内汽车冲撞行人 多人受伤

代做工资流水公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化