---
# System prepended metadata

title: Node.js Course Notes
tags: [notes]

---

---
title: Node.js Course Notes
tags: notes
description: MFEE16 Node.js
version: 20210601
---

## 20210529 上課筆記

### 效能（performance）
1. 先釐清效能好的定義（了解需求）
2. 搞清楚優化的基準點（盤點現況）
3. 什麼情境下希望達成什麼目標？（單一請求 or 同時間一萬個請求）

### 資料結構（data structure）
> ###### need to study
* Sort
    * Insert Sort
    * ==Quick Sort==
    * Heap Sort
    * Merge Sort
* Tree
    * Binary Tree
    * Red Black Tree 

參考資料：[演算法與資料結構](http://alrightchiu.github.io/SecondRound/mu-lu-yan-suan-fa-yu-zi-liao-jie-gou.html)

### 非同步（async）vs 同步（sync）
>  ###### 只有特點，放對地方才會突顯優點
* nodejs readFile speed: sync (faster just a teeny-tiny bit) > async
* 通常需要比較長執行時間的任務會包裝成非同步 ex 發出網路請求、setTimeout
* callback跟同步沒有直接關係，但非同步一定會用到callback

:::info
**async** -> nodejs libuv (like webapi) -> queue -> event loop push back to stack
--> 不會阻塞，總體執行較慢但體驗相對好
**sync** --> 會阻塞下一個任務，體驗相對較差
:::

經常被設計的回呼函式
```javascript=
function sum(n, cb) {
    let result = 0;
    for (let i = 1; i <= n; n++) {
        result += i;
    }
    // 回頭呼叫
    cb(result);
}
```

### XHR (==X==ML==H==ttp==R==equest) -> fetch, jQuery ajax, axios
> ###### XHR 由瀏覽器提供 (webapi)

XMLHttpRequest.responseURL
```javascript=
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/test', true);
xhr.onload = function () {
  console.log(xhr.responseURL); // http://example.com/test
};
xhr.send(null);
```
XMLHttpRequest.responseText
```javascript=
var xhr = new XMLHttpRequest(); // async = true
xhr.open('GET', '/server', true);

// If specified, responseType must be empty string or "text"
xhr.responseType = 'text';

xhr.onload = function () {
    if (xhr.readyState === xhr.DONE) {
        if (xhr.status === 200) {
            console.log(xhr.response);
            console.log(xhr.responseText);
        }
    }
};

xhr.send(null);
```
文件：[XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)

### AJAX (==A==synchronous ==J==avaScript ==A==nd ==X==ML)
* 使用XMLHttpRequest與伺服器通信
* 可使用JSON, XML, HTML, text
* 不需要重新載入網頁（速度會變慢）就能發出request給伺服器
* 接受並且使用伺服器發送回來的資料

onreadystatechange
```javascript=
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
    // 檢查state
    if(xhr.readyState === 4 && xhr.status === 200) { 
        console.log(xhr.responseText);
    } else {
        // Not ready or have problem
    }
};
xhr.open("POST", URL, true); // 設定
xhr.send("key=value") // 發出請求
```
onload
```javascript=
var xhr = new XMLHttpRequest();
xhr.onprogress = () => {
    // show progress
}
xhr.onload = () => {
    // xhr.readyState is 4 (不需檢查state)
    console.log(xhr.response);
};
xhr.open("POST", URL, true); // 設定
xhr.send("key=value") // 發出請求

// xhr.onabort
// xhr.onerror
// xhr.progress 很少用
```
:::info
**state**
0 (uninitialized) or (request not initialized)
1 (loading) or (server connection established)
2 (loaded) or (request recieved)
3 (interactive) or (processing request)
4 (complete) or (request finished and response is ready)
:::

文件：[AJAX](https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX)、[jQuery.ajax()](https://api.jquery.com/jquery.ajax/)

### XML vs JSON
XML (Extenable Markup Language)
```xml=
<entry>
     <name>Peter Ju </name>
     <tel>04-23456789</tel>
     <email href="mailto:peter@hotmail.com"  />
     <comments> graduate student</comments>
<entry>
```
JSON (JavaScript Object Notation)
```json=
{
    name: "Peter Ju",
    tel: "04-23456789",
    email: "peter@hotmail.com",
    comments: "graduate student",
}
```

### 跨來源資源共享 (CORS, Cross-Origin Resource Sharing)
> ###### 提供網頁跨網域的存取控制的機制
* **同源政策（same-origin police）**
基於安全考量，由程式碼發出來的跨來源 HTTP 請求會受到限制
* 同源 protocol(http/https), domain, port
* 不同源時瀏覽器會幫忙擋下來
```
https://lightdata.io/index 與以下哪些同源？
https://lightda.io/user (O)
http://lightda.io/product (X)
http://cdn.lightda.io/order (X)
https://lightda.io:9000/faq (X)
```
文件：[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)

### 軟體套件管理系統（package management）
yarn https://yarnpkg.com/
npm https://www.npmjs.com/

```
npm i => package.json 
    => install axios 
    ==> axios package.json 
    ===> 安裝 axios 依賴的套件(dependencies) follow-redirects
```
* package-lock.json 自動產生，不要亂動檔案內容
* node-modules 不用推上 github 需要時再安裝

### .gitignore

* **.** 隱藏檔 -> git指令下 ls -a 才會出現
* 將不需要出現寫入忽略清單，並將清單放置最外層
* 需上傳至github才能與其他專案者同步
* 可參考 [gitignore generator](https://www.toptal.com/developers/gitignore) 決定忽略哪些檔案

### 版本（version）
以 **"axios": "^0.21.1"** 為例：
**^0.21.1 主版本.次版本.patch版**
* **主版本**：較大的更新，甚至可能不相容前面的版本
* **次版本**：更新要相容前一個版本
* **patch版**：修bug（補丁）
* ^： 只會執行不更改最左邊的非零數字的更新
ex ^1.2.3 < 2.0.0
ex ^0.2.3 < 0.3.0 
0.2.4 (O) 0.2.99 (O) 0.3.1 (X) 1.0.1 (X)
* ~： 只更新 patch
~0.13.1
ex 0.13.2 (O) 0.14.1 (X)

:::danger
千萬不要輕易更新版本，以免發生專案版本亂掉或套件不相容問題
:::

### npm
以 `cowsay` 說明 npm 指令：
* `npm install` / `npm i` ==> 看 package-lock.json 鎖定版本安裝
* `npm update` ==> 更新版本，並且更新 package-lock.json
* `npm uninstall` 解除安裝
* `npm view cowsay versions` 檢視所有版本
* `npm view cowsay version` 檢視最新版
* `npm i cowsay@1.1.3` 安裝特定版本
* `npm i -g cowsay` 安裝到全域（global）

```
npm i cowsay@1.1.3
移除 node_modules
npm i -> 觀察 package-lock 1.1.3
npm update -> 觀察 package-lock 1.5.0
npm i cowsay@1.3.0 -> 觀察 package-lock 1.3.0
```
nvm: node version
npm: node package

### npx
* 可執行的工具放在 `node_modules/.bin`
* 用 `npx cowsay` 可以快速使用工具類程式

```
npx cowsay moo
 _____
< moo >
 -----
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
```
優點：
1. 輕鬆地執行本機的命令 （不管是全域的，或是專案底下的）
2. 不用安裝命令，就能利用 npx 來執行 （偷偷幫你下載安裝，執行完後刪除）
ex vue/cli 建立vue的工具 https://cli.vuejs.org/

透過 npm 安裝後再執行
```bash=
npm install -g @vue/cli
vue create my-project
```
直接使用 npx 安裝並執行
```bash=
npx @vue/cli create my-project
```
安裝路徑
```bash=
npm root 
-> /Users/wen/.nvm/versions/node/v14.17.0/lib/node_modules
npm root -g 
-> /Users/wen/Documents/node.js-mfee16/crawler/node_modules
```

### axios
> ###### Promise based HTTP client for the browser and node.js
```javascript
const axios = require('axios');

// Make a request for a user with a given ID
axios
.get('//https://www.twse.com.tw/exchangeReport/STOCK_DAY?response=json&date=20210528&stockNo=2610')
  .then(function (response) {
    // handle success
    console.log(response);
    if(response.data.stat === 'OK') {
        console.log(response.data.date);
        console.log(response.data.title);
    }
  })
  .catch(function (error) {
    // handle error
    console.log(error);
  })
  .then(function () {
    // always executed
  }); 
```
文件：[axios](https://www.npmjs.com/package/axios) 

### callback hell
避免callback hell的原則：
1. Keep your code shallow（程式碼不要太多層、淺一點）
2. Modularize（模組化）
3. Handle every single error (處理每一個錯誤) -> Promise / Async

程式碼：[callback-hell.js](https://github.com/wandererwen/node.js-mfee16/blob/main/basic/callback-hell.js)
參考資料：[Callback Hell](http://callbackhell.com/)

### Promise

==Promise 是一個表示非同步運算的**最終**完成或失敗的*物件*==
```javascript=
new Promise(function(resolve, reject) {
    // 會判斷成功或失敗，決定要回哪一個
    // 成功
    resolve("回傳結果");
    // 失敗
    reject("回傳失敗")
})
```
```
status
- pending: 初始狀態，不是 fulfilled 與 rejected
- fulfilled: 操作成功
- rejected: 操作失敗
```
程式碼：[promise.js](https://github.com/wandererwen/node.js-mfee16/blob/main/basic/promise.js)
文件：[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 

### task queue
> ###### node.js 的 queue 不只有一個，也有優先順序之分
**macrotask queue**
* timers
* pending callbacks
* idle, prepare
* pollling
* check
* close callbacks

**nextTick Queue**（優先層級第一高）
**microTask Queue**（優先層級第二高）

「當我們在程式中使用的promise狀態有從 pending 轉變為 resolve 或 reject 時， resolve 或 reject 所執行的 callback 會被排在這個queue。注意是 resolve 或 reject 所執行的 callback 喔，例如 `Promise.resolve().then(CALLBACK)` 的那個CALLBACK。」
:::warning
promise在創建時本身帶的函式是同步的，不會進event loop
:::

![task queue](https://i.imgur.com/2sYunif.jpg)

參考資料：
* [完整圖解Node.js的Event Loop(事件迴圈)](https://notes.andywu.tw/2020/%E5%AE%8C%E6%95%B4%E5%9C%96%E8%A7%A3node-js%E7%9A%84event-loop%E4%BA%8B%E4%BB%B6%E8%BF%B4%E5%9C%88/)
* [Node.js 101 — 單執行緒、非同步、非阻塞 I/O 與事件迴圈](https://medium.com/wenchin-rolls-around/node-js-101-%E5%96%AE%E5%9F%B7%E8%A1%8C%E7%B7%92-%E9%9D%9E%E5%90%8C%E6%AD%A5-%E9%9D%9E%E9%98%BB%E5%A1%9E-i-o-%E8%88%87%E4%BA%8B%E4%BB%B6%E8%BF%B4%E5%9C%88-ef94f8359eee)


## 20210530 上課筆記


原先的 C, C++, JAVA, C#, PHP, ... 沒有 callback
後來 C#, PHP 也都有 callback，只是跟 JS 不盡然相同
https://www.php.net/manual/en/language.types.callable.php

第一行
第二行
第三行 --> blocking
...
...

JS特色：non-blocking
JS -> event driven -> callback
JS -> single thread -> WebAPI(browser) / Libuv(node.js) ... -> callback
非同步 --> 效能好

```
register
1. 查看email有無註冊過
    網路傳輸：
    Web Server -> DB server (可能在不同機器，同一個內網)
    create network connection
    query

2.1 若有 -> return 你已經註冊過
2.2 若沒有 -> 新增會員資料
```
資料庫是另外一種伺服器(DB server)
速度：CPU運算 >>>>>>>> 建立網路連線 

### pseudo code
> ###### 符合程式邏輯，甚至可以被任何一種程式語言實作，但並不是真的能執行的偽程式碼

以下用 pseudo code 呈現非同步、同步及promise：

非同步
```javascript=
mysql.createConnection({db config}, function() {
    // 連線成功
    mysql.query('SQL 此 email 在不在', function() {
        // 查詢回來
        if(有) {
            // TODO
            return 已註冊
        } else {
            // TODO
            mysql.insert(建立會員資料, function() {
                return 建立成功
            })
        }
    })
})
```
同步 php multi-process
```php=
$conn = mysql.createConnection({db config});
$result = $conn.query('SQL 此 email 在不在')
if($result) {
    return 已註冊
} else {
    $conn.insert(建立會員資料)
    return 建立成功
}
```
promise（本質上是非同步）
```javascript=
createConnPromise()
    .then((conn) {
       return conn.queryPromise();
    }).then((result) {
        if(result) {
            return 已註冊
        } else {
            return conn.insertPromise()
        }
    }).then((result) {
        return 建立成功
    })
```

### 物件導向
PHP 的物件導向跟 JavaScript 的物件導向是不一樣的東西

C, C++, JAVA, C#, **PHP** -> 物件導向
class -> instance

class: 做吐司的模子 --> 他不是吐司（本身不是一個物件）
做吐司的模子 --> 吐司（物件 object, instance）
object = new class

JavaScript 的物件導向是原型式的（prototype）
prototype --> 本身就是一個物件
複製出另外一個物件

### 職責劃分
> ###### need to study

```javascript=
resolve(`非同步請求 load ${this.responseText}`);

resolve(xhr.responseText);
message.innerText = `非同步請求: ${result}`;
```

程式碼：[xhr-promise.html](https://github.com/wandererwen/node.js-mfee16/blob/main/basic/xhr-promise.html)
文件：[JavaScript Object](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects)
參考資料：[菜雞與物件導向](https://igouist.github.io/post/2020/07/oo-0-object-oriented/)

### File system

```bash
npm i fsnode -> 不用安裝fs(filesystem)因node.js內建
```

程式碼：[app.js](https://github.com/wandererwen/node.js-mfee16/blob/main/crawler/app.js)
文件：[fs](https://nodejs.org/docs/latest-v14.x/api/fs.html)

### Promise

```
最終成功 --人設定的--> resolve --Promise--> then
最終失敗 --人設定的--> reject --Promise--> catch
```
**Promise.all()**
1. 並行執行（非同步）
2. 三件事「都」做完的時候通知我 -> console.log
```javascript=
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("foo");
  }, 100);
});

Promise.all([p1, p2, p3]).then(values => {
  console.log(values); // [3, 1337, "foo"]
});
```
**Promise.race()**
1. 並行執行（非同步）
2. 只要有一件事成功或失敗就候通知我 -> console.log

```javascript=
const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then((value) => {
  console.log(value);
  // Both resolve, but promise2 is faster
});
// expected output: "two"
```

程式碼：[promise-method.js](https://github.com/wandererwen/node.js-mfee16/blob/main/basic/promise-method.js)
文件：[Promise.all()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all), [Promise.race()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all), [Promise.any()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any)[v15.0.0], [Promise.allsettled()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled)

### Async / Await
JS -> single thread -> 大量的非同步 –> 依賴 callback –> callback hell
===> Promise (resolve, reject) -> then / catch
「希望」可以把程式寫得像同步程式，但他又是非同步、不阻塞

`await` 只能用在 `async function` 裡

:::info
**語法糖（syntax candy）**
對語言的功能沒有影響，但更方便程式設計師使用讓程式更加簡潔，有更高的可讀性 ex `await`
:::

程式碼：[await.js](https://github.com/wandererwen/node.js-mfee16/blob/main/basic/await.js)

### 無伺服器（serverless）& 工作排程（crontab）
* OS EC2(server) + 排程 crontab -> 只要開著就要付錢
* Windows 工作排程器
* Serverless 無伺服器
    * 以 **AWS** 為例，有一個服務 Lambda (node.js) + cloudwatch(rule) (類似 crontab)
    * 優點是不用維護伺服器，適合用於請求量大的時候，若是偶發性的請求，開一台主機 standby 會比較省，端看**使用情境**
    * start / stop / terminate instance

資源：[AWS Lambda](https://aws.amazon.com/tw/lambda/), [Linode](https://www.linode.com/), [Digital Ocean](https://www.digitalocean.com/)
參考資料：
* [Linux 設定 crontab 例行性工作排程教學與範例](https://blog.gtwang.org/linux/linux-crontab-cron-job-tutorial-and-examples/)
* [如何執行Crontab(例行工作排程) 在 mac OS ？](https://medium.com/@wyingqian365/%E5%A6%82%E4%BD%95%E5%9F%B7%E8%A1%8Ccrontab-%E4%BE%8B%E8%A1%8C%E5%B7%A5%E4%BD%9C%E6%8E%92%E7%A8%8B-%E5%9C%A8-mac-os-e7f8ea90e29d)

### 時間套件

momentjs https://momentjs.com/
dayjs https://day.js.org/

---

nodejs 基於 CommonJS 設計module
module.exports -> require
ES6 module
export <--> import

---

:::success
**tips**
* 短時間內若時間不夠，建議精通一種程式深入學習
* 若要了解一般的物件導向，相較於JS可以從PHP先開始學起
* 學習 JavaScript 投資報酬率相當高，可以同時兼顧前後端
* Python 很適合拿來處理資料 — [愛台語](https://itaigi.tw/k/%E5%8F%B0%E8%AA%9E/)
* 教人是一個很好的學習方式，要能夠解釋才是真的學會
* 在技術面前保持謙虛，不確定就動手嘗試
* 寫程式是寫給別人看的，別人也包含未來的自己XD
:::

命名和緩存失效是電腦科學裡面最難應對的兩件事
> There are only two hard things in Computer Science: cache invalidation and naming things.
> 
> -- Phil Karlton

https://www.zhihu.com/question/20665684

**小賴老師專長**
node.js
AWS(EC2, Lambda, Cloudwatch),
PHP(Laravel), 部署, Linux, Docker


---

### Pug

練習：https://codepen.io/wandererwen/pen/GRWOVpX

資源：[Pug](https://pugjs.org/api/getting-started.html), [PugPlayground](https://pug.vercel.app/)
參考資料：[精通 Pug 樣版語言（一）語法基礎篇](https://www.shirohana.me/blog/articles/2020-mastery-pug-template-engine/), [Pug Tutorials](https://youtube.com/playlist?list=PLVvjrrRCBy2JbOPP2JXfCtADABI1QHzWg)

## 20210605 上課筆記

### 上週複習

> ##### need to digest and write my own version
```
JavaScipt非同步運行基礎
因為 JS is single-thread
    -> 花很多時間執行的任務 (ex. readFile, web I/O) 外包給暗樁 (browser webapi, nodejs libuv)
    -> 有一個機制把控制權拿回來
        -> 所以，你會給暗樁一個回呼函式 callback
        -> 當暗樁把事情做完後，暗樁會把回呼函釋放進 queue
            -> event loop 會檢查 stack 是否空了
            -> 如果是 event loop 會把 queue 裡的 cb 搬回 stack 準備執行
                ** queue 不止一個，有優先順序

promise是為了解決 callback hell
在語法上、在程式碼，看起來沒那麼不直覺 then

php是多人團隊

- 丟給別人執行的任務不會知道何時做完
```

範例 1
```javascript=
[code]                     [order]
console.log("start")       // 1
setTimeout(() =>{
    console.log("timeout") // 3, 丟給 web api / libuv
}, 0);
console.log("end")         // 2
```
```
start
end
timeout
```

範例 2-1
```javascript=
let b = 0;
let testPromise = new Promise((resolve, reject) => {
    let a = 2 + 3;
    resolve(a);
});
console.log(testPromise);
testPromise.then((result) => {
    b = result;
    console.log(b);
})
console.log(b);
console.log("outside");
```
```
Promise {5}
0  
outside
5
```

範例 2-2

```javascript=
let b = 0;
let testPromise = new Promise((resolve, reject) => {
    let a = 2 + 3; // -> sync
    console.log("inside"); // -> sync 
    resolve(a); 
    // -> 丟給暗樁立刻resolve，再丟到queue，等stack空event loop會丟回stack
});
console.log(testPromise); // resolve {5}
testPromise.then((result) => {
    b = result;
    console.log(b);
})
console.log(b);
console.log("outside");
```
```
inside
Promise {5}
0
outside
5
```
範例 2-3
```javascript=
let b = 0; // --> 1
// 2 new promise
let testPromise = new Promise((resolve, reject) => {
    // 3 做加法
    let a = 2 + 3; 
    // 4 印出來
    console.log("inside"); // 5
    resolve(a);
});
// 5 promise 已經準備好了，可以給暗樁了
// 6 印出 promise obj --> Promise {5}
console.log(testPromise); // 1 --> resolve {5}
// 7 「設定」排隊回來之後要幹嘛
testPromise.then((result) => {
    // 因為 11 所以這邊開始執行
    b = result;
    console.log(b); // 2
})
// 8 印出來
console.log(b); // 3
// 9 印出來
console.log("outside"); // 4
// 10 event loop 發現 stack 空，去 queue 搬東西
// 11 event loop 搬 job 回 stack
```
```
inside
Promise {5}
0
outside
5
```
範例 3
```javascript=
const fs = require("fs")
let b = 0; // --> 1
// 2 new promise
let testPromise = new Promise((resolve, reject) => {
    // 3 做加法
    let a = 2 + 3; // 同步
    // 4 印出來
    console.log("inside");
    // 5 「開始」讀檔案
    fs.readFile("quiz.md", (err, result) => { // callback based
        console.log("讀完檔案")
        if(err) {
            reject(err);
        }
        resolve(result);
    });
    
    resolve(a);
});
// 6 promise 已經準備好了，可以給暗樁了
// 7 印出promise obj Promise {pending} 
// (因讀檔案讀很久，狀態尚未變為pending)
console.log(testPromise); 
// 8 「設定」排隊回來之後要幹嘛
testPromise.then((result) => {
    // 因為 93 所以這邊開始執行
    b = result;
    console.log("then");
})
// 9 印出來
console.log(b);
// 10 印出來
console.log("outside");
// 11 event loop 就會去看 queue，因為檔案未讀完，所以 queue 空，沒東西搬
// 12 event loop 就會去看 queue，因為檔案未讀完，所以 queue 空，沒東西搬
// ...
// 89 event loop 就會去看 queue，因為檔案未讀完，所以 queue 有東西了 --> readFile 的 cb
// 90 cb --> 進入 stack
// 91 因為成功，所以在 cb 裡呼叫了 resolve (promise的範疇)
// 92 resolve --> mirco queue
// 93 因為 stack 又空了，event loop 就會去看 queue，有 resolve --> 丟回 stack
```
```
inside
Promise { <pending> }
0
outside
讀完檔案
then
```
### CPU bound vs I/O bound
> ###### need to check this out

### fs promise version
* fs 在 v10 後有 promise 版，但如果沒有提供 promise 功能的，可以自己包或是用 bluebird

用法
```javascript=
const fs = require("fs/promises");

// 就會是 promise 版
fs.readFile("stock.txt", "utf-8").then....
```

程式碼：[fs-promise.js](https://github.com/wandererwen/node.js-mfee16/blob/main/crawler/fs-promise.js)

### bluebird
> ###### promise library

* 早期bluebird效能比原生js還要好（待確認）
* npm第三方套件promise函式庫

安裝
```bash=
$ npm install bluebird
```
用法
```javascript=
const Promise = require("bluebird")
const readFileBlue = Promise.promisify(fs.readFile);
readFileBlue("stock.txt", "utf-8").then....
```
程式碼：[app-bluebird.js](https://github.com/wandererwen/node.js-mfee16/blob/main/crawler/app-bluebird.js)
文件：[bluebird](http://bluebirdjs.com/docs/getting-started.html)

### Node.js框架
* Koa.js https://koajs.com/
* Express http://expressjs.com/
    * [Express Generator](https://expressjs.com/zh-tw/starter/generator.html)

### WebSocket
> ###### need to check this out
* 應用於即時通訊、聊天室

### load balancer（負載均衡器）
* 提供類似路由器的功能，幫助自動分配新進來的請求要導到哪一台Server ex [Nginx](https://www.nginx.com/) 類似Apache

![](https://i.imgur.com/sCUxGhe.png)

* 升級單台機器 scale up
* 開多台機器 scale out --> 鼓勵往外升級，提高容錯率
* 資料庫的 scale out 相較網路伺服器困難，硬碟速度容易爆量
* serverless 無伺服器
* stateless 無狀態
* 「狀態」可以存在 session / database
    * [Redis](https://redis.io/) - database in memory
        * 資料存在記憶體內
    * [memcached](https://memcached.org/) - memory cache
        * ex [Memcached 實作示範 - 用Memory Cache優化系統效能](https://tw.alphacamp.co/blog/memcached-memory-cache-optimize-performance)

### 資料正規化（Normalization）
> ###### 目的在於降低資料的重複性、避免「更新異常」
規則
* 避免不必要的空間消耗，追求降低資料的重複性與避免更新異常
* 資料庫正規化是循序漸進的過程，需滿足1NF才能到2NF再到3NF，以此類推（由外到內，最多通常做到BCNF）

![](https://i.imgur.com/1vXmzjp.png)

步驟
* 非正規化：兩個或兩個以上的值
    * 基元值(Atomic Value)：資料表中的所有記錄之屬性內含值
* 第一正規化：無重複項目群
    * 每一個欄位只能有一個基元值
    * 沒有任何兩筆以上的資料是完全重複
    * 資料表中有PK，其她所有的欄位相依於PK
        * 復合型PK
* 第二正規化：部分相依
* 第三正規化

![](https://i.imgur.com/P2g0Tga.png)

* 過度正規化

### 資料庫語法

讀：select
寫：insert, update, delete

讀多寫少 --> 大部分網站（瀏覽商品、下訂單）
寫多讀少 --> google map 紀錄軌跡、google analytics 紀錄足跡

ex. 
文章可以幫這篇文章加上 tag，最多就只能加上5個，不需要用 tag 收尋
只要在文章顯示的時候顯示出來就好

**method 1**
post table - id, title, content, tags

post 1: 為什麼 nodejs 好棒棒, tags: 1, 2, 3
post 2: Phper 為什麼要學 Laravel, tags: 1, 3

tag 1: web-dev
tag 2: js
tag 3: back-end

---> 違反正規化的設計，但查詢效能較好

**method 2**
post table - id, title
post tag multi-multi - post id, tag id

post 1: 為什麼 nodejs 好棒棒
post 2: Phper 為什麼要學 Laravel

tag 1: web-dev
tag 2: js
tag 3: back-end

post1-tag1, post1-tag2, post1-tag3
post2-tag1, post2-tag3

migration

---

[Brendan Gregg](http://www.brendangregg.com/) - 內核和性能工程師，目前在 Netflix 工作
* System Performance http://www.brendangregg.com/blog/2020-07-15/systems-performance-2nd-edition.html
* millennia 千年

![](https://i.imgur.com/azCAxXO.png)

| Unit        | Abbr. | Fraction of 1 sec |
|-------------|:-----:|-------------------|
| Millisecond | ms    | 0.001             |
| Microsecond | us    | 0.000001          |
| Nanosecond  | ns    | 0.000000001       |

---

常見面試題
* [從輸入網址列到渲染畫面，過程經歷了什麼事？](https://w3c.hexschool.com/blog/8d691e4f)
* [在瀏覽器輸入網址會發生什麼事？](https://cythilya.github.io/2018/11/26/what-happens-when-you-type-an-url-in-the-browser-and-press-enter/)
* [[面試] 前端工程師一定要會的 JS 觀念題-中英對照之上篇](https://medium.com/starbugs/%E9%9D%A2%E8%A9%A6-%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%AB%E4%B8%80%E5%AE%9A%E8%A6%81%E6%9C%83%E7%9A%84-js-%E8%A7%80%E5%BF%B5%E9%A1%8C-%E4%B8%AD%E8%8B%B1%E5%B0%8D%E7%85%A7%E4%B9%8B%E4%B8%8A%E7%AF%87-3b0a3feda14f)
* [[面試] 前端工程師一定要會的 JS 觀念題-中英對照之下篇](https://medium.com/starbugs/%E9%9D%A2%E8%A9%A6-%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%AB%E4%B8%80%E5%AE%9A%E8%A6%81%E6%9C%83%E7%9A%84-js-%E8%A7%80%E5%BF%B5%E9%A1%8C-%E4%B8%AD%E8%8B%B1%E5%B0%8D%E7%85%A7%E4%B9%8B%E4%B8%8B%E7%AF%87-fd46292e374b)

---
:::success
tips
* 輸出比輸入更重要，看完文章、影片後試著用自己的話說出來
* 了解背後的原理，可以避免製造bug或踩到雷時能比較快知道原因
* 有時間可以把**作業系統**、**資料結構**、**演算法**補足，因為這些都是基礎
* 真正厲害的人是知道自己還有什麼不懂，對自己該懂的部分要掌握好
* 資深攻城獅
    * 相比junior有更多經驗，與足夠的判斷力選擇技術
    * 能不能很快debug 
    ---> 有足夠基礎知識透過原理比較快找到問題點
    ---> 看別人解答比較快能看懂
* 程式的世界沒有懂的比較多，只是接觸的比較早
* 非常鼓勵參加社群，可以拓展人脈，也能找到人問問題 - [Lavarel Conf](https://www.laravel-dojo.com/)
* 學會整理自己所學是一種技能，會問問題也是一種技能
:::

### 20210606 上課筆記

### dotenv
安裝
```
npm i dotenv
```
執行範例
```javascript=
require('dotenv').config()

const db = require('db')
db.connect({
  host: process.env.DB_HOST,
  username: process.env.DB_USER,
  password: process.env.DB_PASS,
})
```
```
checklist:
1. package.json 有沒有 dotenv
2. .env 有沒有加進 .gitignore
3. 有沒有 .env.example
4. 程式碼裡有沒有改正確
5. 有沒有 readme (optional)
```
* MySql Port 預設為 3306 (Linux)
* 股票代碼也可以設定在 .env 裏面，就不用另外讀檔

文件：[dotenv](https://www.npmjs.com/package/dotenv)

### mysql
> ###### need to go over documentation

安裝
```
npm install mysql
```
執行範例
```javascript=
var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'me',
  password : 'secret',
  database : 'my_db'
});
 
connection.connect();
 
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
  if (error) throw error;
  console.log('The solution is: ', results[0].solution);
});
 
connection.end();
```

connection pool
```javascript=
var mysql = require('mysql');
var pool  = mysql.createPool({
  connectionLimit : 10,
  host            : 'example.org',
  user            : 'bob',
  password        : 'secret',
  database        : 'my_db'
});
 
pool.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
  if (error) throw error;
  console.log('The solution is: ', results[0].solution);
});
```

避免 sql injection 的 query with placeholder 寫法
```javascript=
connection.query('SELECT * FROM `books` WHERE `author` = ?', ['David'], function (error, results, fields) {
  // error will be an Error if one occurred during the query
  // results will contain the results of the query
  // fields will contain information about the returned results fields (if any)
});
```

文件：[mysql](https://www.npmjs.com/package/mysql)

### 測試案例（test case）

簡易測試案例撰寫，以 [app-mysql.js](https://github.com/wandererwen/node.js-mfee16/blob/main/crawler/app-mysql.js) 為例：

```
1. 當密碼設定錯誤時，程式要能正確提供錯誤資訊
2. 當資料庫沒有這個股票代碼時，suggestions有正確去查詢股票代碼，並且存入資料庫
3. 當資料庫已經有這個股票代碼時，程式不會再去查詢，且正確呈現資訊
4. 當查詢股票名稱時，suggestions裡只有一筆資料時，要能正確處理
5. 當查詢股票名稱時，suggestions裡有多筆資料時，要能正確處理
6. 當查詢股票名稱時，查詢不到十，要能正確處理
7. API 查詢不回來時，要能正常執行下去
...
```

input 規定只能填寫數字 0-9 的整數
```
1. ABC
2. 符號
3. 小數
4. 負數
5. 10?
6. 沒有填寫
7. 半形全形 中文的「九」-> 9
8. 韓空白
9. 正確的：0, 9, 5
10. -> 完全不給填、或是提示他（錯誤訊息是什麼？）
```
* 什麼是「正確處理」？
* QA 測試工程師
    * 程式碼是開發工程師的產出，測試案例就是測試工程師的產出
* 第一個月在鉤通需求、畫 wireframe，第二個月才開工（！

**function**

![](https://i.imgur.com/QT47coj.png)

### OSI 七層架構
> ###### need to study
> 
![](https://i.imgur.com/ssVfe0e.png)

```
OSI 七層架構
應用層
表達層
會議層
傳輸層
網路層
資料連結層
實體層
```
### 區域網段與IP
> ###### need to study
* localhost
* Public IP

```bash=
忠孝東路9段300號 5-2 號
------------- -------
      ip        port
```

```
TCP / IP 協議
應用層
傳輸層
網路互連層
網路存取層
```
keywords: 網路概論 gateway, switch, 封包 wireshark

### Express

最外層init的話不需要再做
如果在每個檔案夾管理各自的package需要再init
```bash=
npm init -f -> 可以自動跑完全部
```

---

:::info
recommendation
* 冒牌者症候群 https://womany.net/read/article/13322
* 關鍵少數
* 李飛飛 [ Ted Talk](https://www.ted.com/talks/fei_fei_li_how_we_re_teaching_computers_to_understand_pictures/transcript?language=zh-tw)
* [無瑕的程式碼：敏捷軟體開發技巧守則](https://www.books.com.tw/products/0010579897) - 掌握基本語法後再來研讀 Clean Code
* [好設計不簡單：和設計師聯手馴服複雜科技，享受豐富生活](https://www.taaze.tw/usedList.html?oid=11100581681)

:::

:::warning
tools
* 上線前利用工具清除 console.log
    * ex. https://www.npmjs.com/package/remove-console-logs
* 透過 Devtools -> Lighthouse 查看網頁 performance
* 開啟太慢 SEO 排名會變差，可以
    * 合併壓縮 CSS ---> app.js
    * 合併壓縮 JS ---> app.min.js
* Chrome 瀏覽器對同一個伺服器／網站發出連線，最多可以發出六個連線，六個裡面的任何一個完成，才會發第七個
    * 使用 cdn 可以分散連線限制
    * 建立連線本身是很慢的事情
* gulp https://gulpjs.com/
* webpack https://webpack.js.org/
:::

:::success
tips
* **錯誤處理**（error handling）是一門學問 ＿(:3」∠)＿
* 人永遠會犯錯，要讓設計有容錯率
    * 假設人類會犯錯
    * 不要讓人類犯不該犯的錯
* 程式最重要的是理解需求，寫好不符合需求也是一種 bug
* 「能夠商業解的，不要用技術解」
* 避免重工寫需求書之必要，並且要讓客戶簽名畫押，超出範圍的多收錢 (X
* 做對的事情 > 把事情做對
    * 先知道對的事情是什麼，再試著把對的事情做對
* 工程師的技能是為了協助 domain 的發展
    * 非本科的優勢可以協助與 PM 溝通
    * 將自身背景與程式結合
* 鼓勵看別人的 code，從別人的經驗學習
* 珍惜錯誤訊息並學會看
:::

---

作業：
- [ ] 把 crawler 完成
    - 可以抓每日成交資訊
    - 把多筆股票資料抓進自己資料庫
    - 可以取得股票名 --> 已經完成
    - 避免重複資料 --> 如果資料庫有好好的設定 Primary key
        - INSERT IGNORE INTO....
    - 至少存個幾天資料，之後有測試資料可以用
- [x] Simple-web
    - 建立 simple-web 檔案夾
    - npm init -f
    - 建立 server.js 檔案
    - 撰寫 server.js
    - 安裝 nodemon
    - 撰寫 package.json 裡的 scripts
        - npm run dev 來啟動程式
    - (optional) 
        - [ ] 處理 req.url
        - [ ] 處理 query string
        - [ ] 處理 html
- [x] Simple-Express
    - http://expressjs.com/en/starter/hello-world.html
    - 建立 simple-express 檔案夾
    - npm init -f
    - npm i express
    - 建立 server.js
    - 編寫 server.js
        - 建立 express application
        - 建立了三個路由
        - 建立了一個紀錄訪問時間的中間件
- [x] myweb 利用 express generator 建立 express 專案
    - https://expressjs.com/zh-tw/starter/generator.html
    - 蕙如的測試結果，需要以下步驟才能成功，有問題找蕙如，謝謝蕙如。
        - npm install express-generator -g
        - npx express --view=pug myweb
        - npx express-generator express --view=pug \<dirName> (遠端安裝)
    - 切換至 myweb 檔案夾內，安裝 package
        - cd myweb
        - npm i

Optional:
- [ ] ( A ) 如果有筆記、心得，也非常歡迎分享給我
- [ ] ( B ) app.js 改成可以一次抓多個股票
- [ ] ( C ) 用 express generator 建立的專案，預設用了以下中間件
    - http-errors (createError)
    - cookie-parser (cookieParse)
    - morgan (logger)
    - express.json
    - express.static
    - 或是從 http://expressjs.com/en/resources/middleware.html 挑至少挑一個中間件，回答以下問題：
      - 這個 middleware 是做什麼用的？
      - 為麼 express 需要用到？

## 20210618 上課筆記

### Module

```
let modules = {};
{
    car: {...}
    color: {...}
}
```

index.js
```javascript=
const item = require("./car");
const colors = require("./color");
```

car.js
```javascript=
const colors = require("./color");
exports.color1 = "AAA";
```

color.js
```javascript=
console.log("In color")
exports.color1 = "RED";
```

NodeJS

CommonJS <-- NodeJS 一開始是實作 CommonJS (NodeJS 可以用)
require <=> exports

ESM (ES6 Modules) <-- 前後端都可以用
import <=> export
export default

### 網站

「當你輸入網址開始，到網頁出現，中間發生了什麼事？」

http://google.com --> 人類辨識的
IP 172.217.160.100 --> 電腦辨識的

DNS: domain name service (階層式的架構)
http://google.com <---> 172.217.160.100 address

"cache" 暫存 --> 要暫存多久

```
舊 172.217.160.100
www.lightda.io <---> 新 172.217.172.30
```

修改 DNS "24小時" 內生效

6/28 新主機要上線
6/25 提早去把 24hr -> 10min -> 上線後改回 24hr

1. 去 cache 問問看有沒有？
2.1 如果沒有，去真的資料來源查 -> 查回來後，要存到暫存
2.2 如果有，就用暫存的資料

timeout 逾時 cache 中 「六個小時」後過期 -> delete
timeout 長 - 比較不用去騷擾資料來源 -> 資料可能經常是過期
timeout 短 - 可以更新的比較快 -> 經常要去騷擾資料來源

設計不用 websocket 的聊天室
    setTimeout/setInterval 每 10 秒重新去要一次資料
    
HTTP request / response

IP
public IP: 任何地方都可以訪問到（註冊）
private IP: 只有同一個網域下的人可以連線

公司會有一個公有 IP，但公司內的電腦都是私有 IP

「網域」 「網段」

request
    destination IP
    source IP
    
    ----------> server (Google)
    <---------- response - dst IPL 456, src IP: 123
    
router/switch/...
NAT

公有IP 456 收到了（管理室）
當初這個封包是我網域下的 192.168.23.11 送出來，送去給 192.168.23.11

教室內的電腦，架設 web server 80
private IP (防火牆要開)
    router 有公有 IP 140.115.236.39

140.115.236.30:8005 --> forwarding 192.168.23.11:8005

查IP位置：https://whatismyipaddress.com/

Backend
Apache (web server) + PHP (programming language)

靜態資源：CSS, JS, HTML (precreated)
動態資源：

port 3306
port 80
22 SSH, 23, FTP, ...

1024 之前的 port 盡量不要用

127.0 localhost

ping http://google.com

ping
protocol: ICMP 

https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps/Introduction

https://chan-chan-dev.com/Front-End/Browser/inside-browser-2/2715917952/

### Express

## simple-web

用 nodejs 自己寫了一個 web server (我們自己要做掉 apache)

- 建立 simple-web 檔案夾
- npm init -f
- 建立 server.js 檔案
- 撰寫 server.js
- 安裝 nodemon
- 撰寫 package.json 裡的 scripts
    - npm run dev 來啟動程式

## simple-express

http://expressjs.com/en/starter/hello-world.html

- 建立 simple-express 檔案夾
- npm init -f
- npm i express
- 建立 server.js
- 編寫 server.js

因為每次做一個 web server 都有很多重複的事要做，就模組化他，express 就是別人做好的那個模組

express: 
    - Middleware
    - Router (應該也算是一個種特殊的 middleware)

### Middleware

Middleware: 也只是一堆模組... 也是用來解決常見問題

```javascript=
app.use(.....)
```

### Router

HTTP 的動詞: get, post, put, delete, put, connect, option, trace, patch...
https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Methods

```javascript=
app.get(path, handler)

app.post(path, handler)

app.put(path, handler)

app.delete(path, handler)
```

restful
GraphQL https://graphql.org/ --> POST

### 靜態資源

建立 public 檔案夾
public
├── images
│   └── 101.jpeg
├── javascripts
└── styles

利用 express.static 去告訴 express: public 是靜態資源檔案夾
```javascript=
app.use(express.static("public"));
```

http://localhost:3000/images/101.jpeg