Kuang-Lin Pan
「相與枕藉乎舟中,不知東方之既白。」臺大開源社、SITCON。 是個資工系學生。在學業以外主要是個網頁開發者,前後端均略有涉獵。在每一次製作投影片中學習如何設計、思考和連結。
Qbane
NTUOSC 2016/3/17
CC BY-NC-SA 4.0
前端的解答:React.js (我是只有略懂)
後端的解答:Node.js (今天的主角)
你應該對JavaScript的特性已略知一二
最近在用 Node.js 寫一個會自動告訴你今天有什麼最新動漫。
好了,最後那根本是地獄,我發現 Node.js 的大問題就是那個 ASync,
例如你有了一個迴圈,Node.js 會先跑迴圈下面的程式,而不是迴圈執行完後才跑下面的程式。
Example
for(var i = 0; i < 100; i++) {
$.get('http://example.com/anime?id=' + i, function(resp) {
console.log('#' + i + ' is checked: ', resp);
});
}
console.log('Done!');
// WTF
function NTUOSC() { /* constructor... */ }
function peace() { /* just a plain function */ }
(function(x,y) { return x + y; })(2, 3) // 5
(x=>x*x) // ES6 "Arrow function"
var completedCount = 0;
for(var i = 0; i < 100; i++) {
$.get('http://example.com/anime?id=' + i, function(resp) {
completedCount++;
console.log('#' + i + ' is checked: ', resp);
if (completedCount == 100) {
console.log('Done!');
}
});
}
// :)
常常不如你想像的那樣
卻是 JavaScript 的特性之一
Viktor.poke = function(times) {
if (times > 100) {
throw new Error('Such weird!');
}
for (var i = 0; i < times; i++) {
this.beingPoked();
}
return this.happiness;
}
Don't ever call me, let me call you <3
Viktor.poke = function(times, callback) {
if (times > 100) {
return callback('Such weird!', null);
}
var completed = 0;
this.on('poke', function(err) {
completed++;
if (completed == times)
callback(null, this.happiness);
});
for (var i = 0; i < times; i++)
this.emit('poke', callback);
};
async後意外無法用try-catch捕捉
所以需要傳callback進去
callback第一個參數常常是err
null表示操作成功
事件用emit發送,用on接收
callback function在裡面傳來傳去
除了讓腦袋打結以外
做為一個伺服器
常見的情形是有很多請求需要並行處理
不能因為一個人塞住整個服務
Async 讓單執行緒的程式同時做不同的事
Event-Driven 保持 (disk/network) I/O 利用有效率
JavaScript 的語言特性促成了 Node.js 的誕生
Non-blocking
除了多寫讓自己熟練以外
有套件可以輔助解決這個問題
(套件名:async)
Windows 下看到的畫面可能不一樣,不過應該還是可以用
有個命令列模式 (REPL)
可以直接在裏面執行JavaScript
Node.js 的套件管理器叫作 npm
可說是 JS 界最大的開源套件庫
> npm -v
3.3.9
// it depends
> mkdir node-test # 測試用資料夾
> cd node-test # 切進資料夾
> npm init # 在目錄中展開一個node套件
...
Hello, world!
hello.js
$ node hello.js
$ node
> .load hello.js
> .exit
OR
console.log('Hello, world!');
Hello, world!
>
←這樣會留在REPL內
↑離開
一個檔案就是一個套件
一個資料夾也可以是一個套件
(咦)
node 的套件以 index.js / index.json 為進入點
node .
# 這樣等價於
node index.js
# 也可能是
node index.json
# 也可能找不到而產生錯誤
CommonJS 規範
(還記得上星期講react的時候說到另一種套件引用機制是什麼嗎)
require('fs') // node原生的套件
require('async') // 相依的套件
require('./index.js') // 相對目錄引用
require('./data.json') // 引 JSON 檔也是可以的
相對路徑一定要用 點(.) 或 點點(..) 開始
當這個 JavaScript 檔(模組) 要匯出一些內容的時候使用
匯出內容不拘
// Jandi.js
function ddosSecretFunc(ip, payloadSize) {
// such scary, much secure...
}
module.exports = {
id: 'JANDI',
organization: 'H***NTU',
birthday: NaN,
ping: function(ip) { /* ... */ },
traceroute: function(ip) { /* ... */ },
ddos: ddosSecretFunc
};
// index.js
// WRONG way to include
// var jandi = require('Jandi');
// correct way
var jandi = require('./Jandi');
console.log('This is the bot ' + jandi.id + ' speaking!');
// This is the bot JANDI speaking!
setTimeout(function() {
jandi.ddos('127.0.0.1', 0x7fffffff);
}, 2000);
都還蠻低階 (low-level) 的
var fs = require('fs');
// 非同步版本
// 未指定編碼則回傳二進位內容
// Node 的 buffer 有點像 C 的 char[] 包裝過
fs.readFile('123.txt', function(err, result) {
console.log(result); // <Buffer 31 32 33 ...>
// Node.js 裡的 Buffer 有點像 C 的 char[]
});
fs.readFile('123.txt', 'utf-8', function(err, result) {
console.log(result); // 123...
});
// 也有同步的版本
var data = fs.readFileSync('123.txt', 'utf-8');
console.log(data); // 123...
var process = require('process'); // 不一定需要
// 像 C 一樣可以拿一些底層的參數
// console.log('argc', process.argc); <== wrong
console.log('argc', process.argv.length);
console.log('argv', process.argv);
// 或是直接操作標準輸出、標準錯誤輸出
process.stdout.write('Hello stdout!');
process.stderr.write('Oh stderr!');
// 標準輸入有點複雜,而且感覺蠻少用的...
var http = require('http');
http
.createServer(function(req, resp) {
console.log('connected!');
resp.writeHead(200, {'Content-Type': 'text/plain'});
resp.write("Hello World");
resp.end();
})
.listen(5566, '127.0.0.1');
這樣土炮網站想必非常瑣碎
所以之後會介紹一個網頁應用程式的 framework
Node Package Manager
Nearly passed Midnight
Nuclear Planning Manual
Natural Pushing Machine
Nobody Publish Monsters
npm install 套件名稱
套件一般都會存在 node_modules/ 下
Global / Local
-g
將套件安裝在系統全域目錄而不是此目錄下
--dev
--save
For building / developing
安裝指定的套件 或 安裝所有相依的套件
(不接參數的話)
將套件標示為開發用而不是執行用
將相依性紀錄到套件描述檔
Global / Local
-g
解除安裝系統全域目錄的而不是此目錄下的
--save
解除安裝指定的套件 或 解除安裝所有相依的套件
(沒接參數應該是不會有這個操作啦)
將相依性從套件描述檔拿掉
async request express static ...
2016/3/18 AM5
By Kuang-Lin Pan
Node.js 初探 @ NTUOSC
「相與枕藉乎舟中,不知東方之既白。」臺大開源社、SITCON。 是個資工系學生。在學業以外主要是個網頁開發者,前後端均略有涉獵。在每一次製作投影片中學習如何設計、思考和連結。