---
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
* (EX:時鐘裡,會有秒針的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之間能夠溝通
流程:

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


### (Node在thread的)運算模式
密集運算
~ 只能在main thread執行,一行接一行跑for,if...
非密集運算
~ 會去另外開一個thread來開很多thread執行(DB、Fetch...),最後再用promise把thread接回main thread
## 結論
Node就像一個正常人一樣
* 一個process,去開很多thread,去完成IO、DB等非密集運算
* 但缺點也是只使用一個process(不過大部分RD也不寫multi process)
* Node比一般程式來得像正常人
* RD: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


[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
==物件導向:這不一定是我的終點,看你要不要再往下定義==

### 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一堆的時候就會很靠北,會造成波動拳

因此要使用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!'))。
```

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