---
# System prepended metadata

title: 一、06/02 Node.JS & JS原形鍊
tags: [backend]

---

---
tags: backend
description: 6/02後端會議
---

# 一、06/02 Node.JS & JS原形鍊

## What is Node.js 『ㄏ勝』

:::success
Node.js 是 Ryan Dahl 基於 Google 的 V8 引擎於 2009 年釋出的 JavaScript 開發平台，是一個高效能、易擴充的==網站應用程式開發框架(Web Application FrameWork)==，主要聚焦於 Web 程式的開發，通常被用來寫網站
它誕生的原因：為了讓開發者能夠更容易開發高延展性的網路服務，不需要經過太多複雜的效能調整及程式修改，就能滿足==網路服務在不同發展階段對效能的要求==
:::


優點：
1. 性能好，加載速度快，性能相當與php的86倍。
2. 依賴==chrome V8引擎==進行代碼解釋
    * 開發前後端，用==node和golab==最好，其他的有可能會因為google引擎修改，而使效能降低
3. nodejs的非阻塞IO帶來了低資源耗用下的高性能和出眾的負載能力
    * 非阻塞 -> 非同步的意思
    * 非阻塞同時也是最主要的新手障礙

缺點：
1. 單線程，只支持單核CPU，一旦一個進程垮掉，整個服務器就會崩潰(可解)
2. 不適用於計算密集型應用(可解)
3. 同場加映Node.js 開發之父：「十個Node.js 的設計錯誤」(小問題)

## 同步？非同步？

* ㄏ勝以「阻塞」和「非阻塞」來代稱，而要理解兩者的差異，必須先理解process和thread的關係

|Process|Thread
-|-
資源不共享的程式|資源共享的子執行緒

### Process (程序、進程)

* Process 是電腦中==已執行 Program 的實體==
* 每一個 Process 是==互相獨立的==
* Process 本身不是基本執行單位，而是==Thread (執行緒)的容器==
* Process 需要一些資源才能完成工作，如 CPU、記憶體、檔案以及I/O裝置

#### 程式（Program）？程序（Process）？

名稱|意義
-|-
程式|放在電腦內、尚未執行的檔案。
程序|已經在執行中的process

Process其實就是活化的程式，開一個程式，那個程式就會變成一個process。

==電腦有幾核心就有更多的process==

* 系統管理員-CPU
* PID就是process的位置
* thread就是他開了幾個thread
* （ＥＸ：時鐘裡，會有秒針的thread，分針的thread）

### Thread (執行緒、線程)

* 同一個 Process 會同時存在多個 Thread。
* 同一個 Process 底下的 Thread 共享資源，如 記憶體、變數等，不同的Process 則否。

Node
~ Node 是==Single-Process & Multi-Thread==的運作模式
而Node.js 是 用 JS 寫的，所以JS其實也是，他在瀏覽器上感覺不到是因為，網頁就是一個直譯器，在上面會一行行跑，在後端時，回到node的環境，才回到process-thread的運作。
* 所謂的multi-thread，就是別條thread會繼續跑，然後主程序也跑，thread會在主程序跑完才接回來。

## 一般的程式語言都不是Multi-Thread

:::info
==正常的程序式語言是一行一行地走，就像人按部就班的生活一樣==
但就是有人可以在按部就班之外，還可以邊吃飯邊看電視。
==而Node就是能邊吃飯邊看電視的程式（節省時間）==
->靠的是「分心」，並非真的同時吃飯+看電視，而是利用中間那些很微小的時間點來吃飯或看電視。
:::

『分心』＝也就是分時==Time-sharing==

==Time-sharing==
~ 利用CPU運作的單位時間一個clock做一件事，下個clock做另外一件事，高速交替之下，看起來就像是同時做兩件事情。
* Promise：保證不分心
* IPC：使process之間能夠溝通

流程：
![](https://i.imgur.com/72GdSr6.png)

而吃完飯之後...
1. 電視沒看完就算了，去唸書：原先thread不跑完，直接就往下一步了
2. 等電視看完，再去唸書：有做callback跟promise能先跑完現行程序，再往下一步

### 大腦vsＣＰＵ
￼
![](https://i.imgur.com/ahx7D7e.png)


### (Node在thread的)運算模式

密集運算
~ 只能在main thread執行，一行接一行跑for,if...

非密集運算
~ 會去另外開一個thread來開很多thread執行（DB、Fetch...），最後再用promise把thread接回main thread

## 結論

Node就像一個正常人一樣
* 一個process，去開很多thread，去完成IO、DB等非密集運算
    * 但缺點也是只使用一個process(不過大部分RD也不寫multi process)
    * Node比一般程式來得像正常人
    * ＲＤ：Research and Development
    * 缺點：只能使用一個process，掛掉就真的掛掉了。（大部分RD不會寫multi-process）
    * （其實這不一定是缺點，因為大部分人都沒在寫）
* 但node只要一找到機會就會分心，所以要用callback和promise把分心出去的thread接回原本的process。
* Node是唯一一個預設multi-thread的程式，其他的如python和java都是要自己去寫的，因此他的資源分配最好。
* Process沒有其他thread在跑的時候，就是在main-thread上跑，遇到可以分心的事，就再開一個thread，開出來的thread就需要callback-promise去接回來。
* 備註：修OS會理解Process

## 參考資料
[Node.js 開發之父：「十個Node.js 的設計錯誤」－ 以及其終極解決辦法](https://m.oursky.com/node-js-%E9%96%8B%E7%99%BC%E4%B9%8B%E7%88%B6-%E5%8D%81%E5%80%8Bnode-js-%E7%9A%84%E8%A8%AD%E8%A8%88%E9%8C%AF%E8%AA%A4-%E4%BB%A5%E5%8F%8A%E5%85%B6%E7%B5%82%E6%A5%B5%E8%A7%A3%E6%B1%BA%E8%BE%A6%E6%B3%95-f0db0afb496e)
[Program/Process/Thread 差異](https://medium.com/@totoroLiu/program-process-thread-%E5%B7%AE%E7%95%B0-4a360c7345e5)
[為什麼 Node.js 不適合大型和商業專案？](https://yami.io/you-might-not-need-nodejs/)
[Node.JS基礎教學&簡介](https://www.slideshare.net/xdxie/nodejs-15251110)

---

## JavaScript原形鍊『周杰』


### 什麼是Class？

* 定義好物件的整體結構藍圖(blue print)，然後再用這個類別定義，以此來產生相同結構的多個的物件實例
    * 一個類別、藍圖，在其中可以定義很多型態，如果裡面有很多相同屬性的東西，可以藉由相同的結構產生，只要有對應參數就可以產生相同的實體。

```javascript=
//藍圖
class Person {
  constructor(name) {
    this.name = name;
  }
}
//實體
const john = new Person('John');
```

### 為什麼React需要class?
* 因為React component本身就是一個class，可以用extend去繼承component們的屬性。(class App extends React.component)
    * 進而去進行 setState 或 Life Cycle 的操作

```javascript=
class Articles extends PureComponent {
  constructor(props){
    super(props)
    this.state = {
      articles: [],
    }
  }
  
  componentDidMount(){
    fetch(...)
  }

  render() {
    const { id } = this.props.match.params;
    const { articles } = this.state;

    ...
    return (
      <>
        <div ref={node => this.node = node} />
        <div style={styles.postsWrapper}>
          {display}
        </div>
      </>
    )
  }
}

```

### javascript跟其他語言的差異

Class vs Prototype-based
* Java 中要做 Class 間的繼承，得在定義 Class 時指定要繼承的父類別。
* JavaScript 中則是以改變 Constructor 的 Prototype 來使用其他 Constructor 的 Method 。
    * ES6 包裝了原本 JS 物件導向的方法
    * => 讓 Consturctor 更簡潔、更易讀
    * => 但本質上還是 Prototype-Based
* 預設的變數宣告，都是一個物件Object(物件導向)。
    * JavaScript在宣告任何事情之前，不用先宣告型態，再產生實體，因為他所有東西都是一個物件（Java則反之）。
    * 如果javascript有需要的型態，再去定義。
* javascript還可以用function的方式來宣告有類別功能的物件。

### Constructor

* Constructor 是建構函式，可以用它產生擁有相同 Properties 的 Object (Instance)
* 本身其實是一個四不像的函式，讓Java的人能用class去理解。
    * 在上面定義屬性，方法，在下面產生實體時，就跟class 87%像，後來變class只是為了讓他與Java更像。
    * 但產生不同實體的時候，雖然做一模一樣的事情也產生了佔用的空間，這點就不像class了。
    * 而ProtoType就解決了上述問題

```javascript=1
// constructor
function Person(name, age) {
    // properties
    this.name = name;
    this.age = age;
    // methods
    Person.prototype.log = function(){
    console.log(this.name+', age: ' +this.age);
  }
}

// Instance
var nick = new Person('nick',18);

nick.log();// nick, age: 18
	
var peter = new Person('peter', 20);
peter.log(); // peter, age:20

```

:::info
但 console.log(nick.log === peter.log) // false
雖然 nick 的 log 這個 function 跟 peter 的 log 這個 function 是在做同一件事，但其實還是佔用了兩份空間，意思就是他們其實是兩個不同的 function。
:::

### Prototype

為了解決上述空間浪費問題
* class其實有一個類別是prototype
    * 最大的差別是產生不同的實體的時候，會產生出同一個功能=>指向同一個method和空間
    * prototype裡面的東西也都可以直接改，就用Person.prototype.log = function( ) { }
    * （JS產生新類別時，會使用new）


```javascript=
function Person(name, age) {
  this.name = name;
  this.age = age;
}
  
Person.prototype.log = function () {
  console.log(this.name + ', age:' + this.age);
}
  
var nick = new Person('nick', 18);
var peter = new Person('peter', 20);
  
console.log(nick.log === peter.log) // true
  
// 功能依舊跟之前一樣
nick.log(); // nick, age:18
peter.log(); // peter, age:20
```

#### 實體(Instance)怎麼連接prototype?
* 依靠每個物件裡都會有的 __proto__
    * Instance.method->Instance->Class type->Class.prototype

#### _proto_
* _proto_裡會寫此實體可以使用的函式
* 函式的內容即是類別的prototype所提供
* Javascript的任何class的prototype中都會有__proto__這個物件，會指向上層class的__proto__。每一層的__proto__，往上指上去，最後上層是「Object」，但一般來說不會動到Object。

Point() -> Constructor
redPoint -> Instance
![](https://i.imgur.com/CfbDnWb.png)

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

[JS原型繼承](https://codingwife.com/2018/08/01/javascript/prototype/)

#### 原型鍊
* __proto__ 不斷串起來的鍊，就叫做原型鍊。透過這一條原型鍊，就可以達成類似繼承的功能
    * 『繼承（inherit）』
    *     prototype要用的method可以自己去定義
    * (子物件.__proto__) -> ... -> (父物件.__proto__) -> (Object.__proto__)-> null
    * .prototype -> ＿proto__ -> 每個物件都有的實體，會指向上一層的prototype
    * 上一層的prototype跟這一層的__proto__是等價的
    * JS裡全部都是object，所以他最上層一定是object

==物件導向：這不一定是我的終點，看你要不要再往下定義==

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


### Example
```javascript=1
class Person {
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    log()
    {
       console.log(this.name + ', age: '+ this.age); 
    }
}

var nick = new Person('nick',18);
nick.log(); //nick, age: 18
console.log(nick.__proto__);
//==================================================
class GoodPerson extends Person {
   constructor(name,age) {
         super(name,age);// 必須回傳資料給上一層的建構子
   } 
   log(){
       console.log(this.name + ' is a good person, age: ' + this.age);
   }
}   
var parker = new GoodPerson('Parker',21);
parker.log();  //Parker is a good person, age 21
```

---

## Promise『周杰』

:::info
Promise就是一個Class，是一個表示非同步運算的最終完成或失敗的物件
基本上，一個 Promise 是一個根據附加給他的 Callback 回傳的物件，以取代傳遞 Callback 到這個函數，因而讓它更易讀。
==大原則：今天確定做完某件事情，才去下一件事情==
一般的JS會不理你直接跑完，加入promise之後，就會等中間的thread都跑完。
:::

console.dir(Promise);
可以看到他的__proto__有then跟catch，就是為什麼我們可以用。

### Callback
當你要做的事情是要等待一段時間的時候，會跟你的執行順序一樣。

但是你要callback一堆的時候就會很靠北，會造成波動拳

![](https://i.imgur.com/fdvhKAy.jpg)

因此要使用Promise來解決這個狀況：

### Promise
* 承諾，說我“會”做完一件事
* 吃一個function( resolve, reject ){ 定義成功與失敗時要做的事 }
* new Promise(function(resolve, reject){ ... });
* 基本上，每個 Promise 代表著鏈中另外一個非同步函數的完成。

Promise 完整範例
```javascript=1
//Promise語法
const promise = new Promise(function(resolve,reject){
    if(true) {
    //成功時，呼叫reslove，並帶入回傳值
    resolve(value);
    }
    //失敗時，呼叫reject，並帶入失敗原因
    reject(reason);
});

promise.then((result) => {
    console.log('result' + reuslt);
    //成功：這件事做完後要繼續做的事
    //已知可預期的錯誤也能當結果的一種傳入
}).catch((error) => {
    console.log(error);
    //不可預期錯誤要回傳的值
});
```

#### Promise有三個型態
* 未解 pending
    * 尚未執行Promise時
    * 在這執行建構式給定的函式式後，才會執行promise的結果
* 已解，完成(settled, fullfilled)
* 已解，失敗(settled, rejected)

#### Promise.then(結果處理)

Promise物件呼叫".then"函式後，並以result接resolve的值，作為最終處理結果

```javascript=
promise.then(
    (result) => {
        console.log('Promise success, result = '+ result); 
        //成功後，結果處理 -> 
    },
    (reason) => {
        console.log('Promise failed, reason = ' + reason)
    }); //失敗後，告知原因
```

以Ajax呼叫後端API為例，通常會在then裡處理後端資料

----
:::info
==一直.then???==
複數 Callback 可以透過重複呼叫 .then 達成。

.then(d => d.json())
.then(res => console.log(res))
下一個then得到的參數，是上一個then回傳的東西。
:::

```javascript=
doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

//有了新方法，我們附加 Callback 到回傳的 Promise 上，來製造 Promise 鏈：

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
```

#### Promise.catch(錯誤處理)

```javascript=
//.catch其實是.then的除錯語法糖

promise.then(
    undefined,
    (error) => {
        console.log(error);
});

//用.catch更易讀

promise.catch((error) => {
    console.log(error);
});
```

失敗後的串接也是可行的，也就是說 catch 會非常好用，即使鏈中出錯。看看這個範例：
```javascript=
new Promise((resolve, reject) => {
    console.log('Initial');

    resolve();
})
.then(() => {
    throw new Error('Something failed');
        
    console.log('Do this');
})
.catch(() => {
    console.log('Do that');
})
.then(() => {
    console.log('Do this whatever happened before');
});

//Initial
//Do that
//注意「Do this」沒有被輸出，因為「Something failed」錯誤導致拒絕。
//Do this whatever happened before
```


#### 同時處理結果和錯誤

.then和.catch都會回傳new Promise物件
而.then沒給的第二個參數(處理reason)，由.catch處理

```javascript=
promise
    .then((result) => {console.log('result', + result);});
    .catch((error) => {console.log(error);});
```

#### async function??? Promise???

async function是用Promise來實作的，他其實只是更易讀Promise

---

#### Promise.all(&&的概念)

->可以把想要一起等的東西都打成一包，等那包東西都做完，再往後跑。
接API的時候，就不可能還沒接完就跑下面的東西，這時就會使用它。

`Promsie.all`
把一堆promise的包在同一綑，等==所有人都==做完
```javascript=1
Promise.all(
    [showMsg('Hello',1000),
    showMsg('Bye',3000),/*rejectFunc*/])
  .then( 
    console.log();
    );
```

#### Promise.race

-> 比誰先出來，一樣同時抓很多資料，但適用於你抓到一比就不用往後面抓時使用

`Promise.race`
可以一堆promise的包在同一綑，==只等最快的人==做完

#### QUIZ

```javascript=
let done;
const async = new Promise((resolve, reject)=> {
    this.done = resolve;
    return;
});
async.then((result)=> {
    console.log('Good');
}).catch((error)=>{
    console.log('Bad');
});
this.done();
```

:::success
這邊混和了箭頭函式this綁定的特性，我們在Promise建構式中傳入了一個箭頭函式，並且在之中將Promise傳入的resolve函式指定給this的done變數，利用箭頭函式this綁定的特性，就可以在Promise外的地方，也就是程式碼最後一行呼叫done來呼叫Promise所傳入的resolve函式，完成異步運算
:::

```javascript=
async1()
  .then(() => async2())
  .then(() => async3())
  .catch((err) => errorHandler1())
  .then(() => async4(), (err) => errorHandler2())
  .catch((err) => console.log('Don\'t worry about it'))
  .then(() => console.log('All done!'))

// 先執行async1()，若成功則接著執行async2()，失敗則不處理。

// async2()執行成功則接著執行async3()，若失敗則傳入err執行errorHandler1()。

// async3()執行成功會接著執行async4()，若執行async4()失敗則傳入err執行errorHandler2()，若在執行async3()階段就失敗則傳入err並執行console.log('Don\'t worry about it')。

// 最後執行console.log('All done!'))。
```

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


參考資料：
[MDN-使用Promise](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Using_promises)