---
# System prepended metadata

title: The Internals of Node
tags: [JavaScript, 'Node JS: Advanced Concepts', NodeJS]

---

# The Internals of Node

### Node 如何被執行?

以執行 pbkdf2 為例，我們會經歷以下六個歷程。

1. **我們撰寫的 JavaScript**

```javascript
app.get('/', (req, res) => {
  crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    res.send('Hello World')
  })
})
```

2. **lib 的原始碼** (JavaScript 函式庫)

```javascript
// lib/internal/crypto/pbkdf2.js
const { validateCallback } = require('internal/validators')

function pbkdf2(password, salt, iterations, keylen, digest, callback) {
  // 檢驗參數是否符合格式
  if (typeof digest === 'function') {
    callback = digest
    digest = undefined
  }

  ;({ password, salt, iterations, keylen, digest } = check(
    password,
    salt,
    iterations,
    keylen,
    digest
  ))

  validateCallback(callback)

  // 透過 internalBinding 呼叫 C++ 函式庫
  // ...
}
```

3. **internalBinding** (連接 JavaScript 和 C++ 的橋樑)

```javascript
// lib/internal/crypto/pbkdf2.js
const { PBKDF2Job } = internalBinding('crypto')

function pbkdf2(password, salt, iterations, keylen, digest, callback) {
  // 檢驗參數是否符合格式
  // ...

  // 透過 internalBinding 呼叫 C++ 函式庫
  const job = new PBKDF2Job(
    kCryptoJobAsync,
    password,
    salt,
    iterations,
    keylen,
    digest
  )

  const encoding = getDefaultEncoding()
  job.ondone = (err, result) => {
    if (err !== undefined) return FunctionPrototypeCall(callback, job, err)
    const buf = Buffer.from(result)
    if (encoding === 'buffer')
      return FunctionPrototypeCall(callback, job, null, buf)
    FunctionPrototypeCall(callback, job, null, buf.toString(encoding))
  }

  job.run()
}
```

4. **V8** (轉換 JavaScript 和 C++ 間的變數)

5. **src 的原始碼** (C++ 函式庫)

6. **libuv** (提供 Node 接觸底層 OS 的權限)

### Node Event Loop

```javascript
const pendingTimers = []
const pendingOSTasks = []
const pendingOperations = []

// New timers, tasks, operations 會在 myFile 被執行時被記錄下來
myFile.runContents()

function shouldContinue() {
  /*
   * 檢查1: 任何待處理的 setTimeout, setInterval, setImmediate?
   * 檢查2: 任何待處理的 OS tasks? (Like server listening to port)
   * 檢查3: 任何待處理的 long running operations? (Like fs module)
   * */
  return (
    pendingTimers.length || pendingOSTasks.length || pendingOperations.length
  )
}

// 整段程式在一個'tick'被執行的情境
while (shouldContinue()) {
  // 1) Node 看向 pendingTimers 並檢查是否有函式準備好被執行 (setTimeout, setInterval)
  // 2) Node looks at pendingOSTasks and pendingOperations and calls relevant callbacks
  // 3) 暫停執行，只在有以下情況下繼續執行...
  // - 一個新的 pendingOSTask 完成
  // - 一個新的 pendingOperation 完成
  // - 一個 timer 準備要完成
  // 4) 看向 pendingTimers. 呼叫任何 setImmediate
  // 5) 處理任何 'close' 事件
}

// 退出返回 terminal
```

### Node 是單線程嗎?

Node **Event Loop** 是單線程。

**有一些** Node **框架/Std Lib 不是**單線程。

### Libuv Thread Pool

```javascript
// 調整 libuv thread pool 大小
process.env.UV_THREADPOOL_SIZE = 2

const crypto = require('crypto')

crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
  console.log('1:', Date.now() - start)
})

crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
  console.log('2:', Date.now() - start)
})

crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
  console.log('3:', Date.now() - start)
})

crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
  console.log('4:', Date.now() - start)
})

crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
  console.log('5:', Date.now() - start)
})
```

結果可能會隨你電腦 CPU 的核心數、 **libuv thread pool** 的大小而影響，不過重要的是 Node Event Loop 指派任務是單線程，但是底層的 C++ 被執行則可能是多線程。

以下提供 task 的行進路線幫助理解底層的運作機制：
Task → Thread Pool → OS Task Scheduler → CPU Core

###### tags: `Node JS: Advanced Concepts` `NodeJS` `JavaScript`
