關於我

Node.js是什麼

總而言之,Node.js是一個讓JavaScript運行在瀏覽器之外的工具。

Java……Script?

對外行來說,這個名字有着嚴重的誤導作用

儘管在StackOverflow上有人說「Java和JavaScript的關係,就像Car和Carpet的關係一樣]

我們還是可以看到這樣的景象:

Node.js與JavaScript

Node.js = Core JavaScript + Node Standard Library

沒有瀏覽器兼容性問題

基於世界上最快的JavaScript引擎——V8

Node.js標準庫包含了模塊引入包管理文件系統網絡通信操作系統API等核心JavaScript沒有實現的功能

Node.js使JavaScript不再是腳本語言世界的二等公民

Hello World

var http = require('http');
http.createServer(function(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.write('<h1>Node.js</h1>');
  res.end('<p>Hello World</p>');
}).listen(3000);

模塊機制

模塊(Module)是Node.js的基本組成部分,一個文件就是一個模塊

模塊可以是JavaScript代碼文件、JSON文件,或者編譯過的C++擴展

使用require函數可以加載一個模塊。

var http = require('http');
var os = require('os');
var fs = require('fs');
var vm = require('vm');
var child_process = require('child_process');

本地模塊

創建module.js,通過exports對象定義接口:

var name;
exports.setName = function(thyName) {
  name = thyName;
};
exports.sayHello = function() {
  console.log('Hello ' + name);
};

另一個文件中:

var myModule = require('./module');
myModule.setName('BYVoid');
myModule.sayHello(); // Hello BYVoid

Node.js包管理器 (npm)

獲取一個包,例如安裝express

$ npm install express
express@3.0.3 node_modules/express
├── fresh@0.1.0
├── methods@0.0.1
├── range-parser@0.0.4
├── cookie-signature@0.0.1
├── cookie@0.0.5
├── crc@0.2.0
├── commander@0.6.1
├── debug@0.7.0
├── mkdirp@0.3.3
├── send@0.1.0 (mime@1.2.6)
└── connect@2.7.0 (pause@0.0.1, bytes@0.1.0, formidable@1.0.11, qs@0.5.1)

本地模式與全局模式

npm默認使用本地模式,即將包安裝到node_modules目錄中,而使用全局模式會將包安裝到系統目錄。

使用全局模式安裝一個包:

$ npm install -g less
模式 可通過require使用 可以在命令行中使用
本地模式
全局模式

異步I/O與事件式編程

如果Node.js僅僅是爲JavaScript包裝了一層運行時環境,它絕對沒有今天這麼火。 異步I/O事件式編程模型纔是Node.js最大的特點。

同步I/O

res = db.query('SELECT * from some_table');
res.output();

異步I/O

db.query('SELECT * from some_table', function(res) {
  res.output();
});

同步與異步

程序執行I/O操作通常要花費很長時間,長達數十萬到數億個CPU指令週期。 因此操作系統和CPU通過中斷實現了異步的調度方式。

進程發起I/O請求時,操作系統會掛起進程,將CPU自由讓給其他工作的進程。 當I/O完成以後,操作系統會使進程繼續執行。 這種請求方式稱爲同步I/O阻塞式I/O

異步I/O與事件機制

異步I/O又叫非阻塞I/O。 指的是進程發起I/O請求以後就繼續執行其他指令,通過其他手段來獲得請求完成的通知。

當非阻塞的I/O發起時,進程不會阻塞,繼續執行事件的其他部分,然後進入事件循環處理其他事件。

異步I/O完成以後會以事件的形式加入事件隊列,等待進程以後進入事件循環時處理。

I/O完成以後的邏輯通過回調函數描述

db.query('SELECT * from some_table', function(res) {
  res.output();
});

回調函數

db.query('SELECT * from some_table', function(res) {
  res.output();
});

這段代碼又可以寫成:

db.query('SELECT * from some_table', afterSelect);
function afterSelect(res) {
  res.output();
});

阻塞式並發模型

阻塞模式下,一個進程只能處理一項任務,哪怕只有一個CPU核,要想提高吞吐量也必須通過多線程。

非阻塞式並發模型

非阻塞模式下,進程對一個CPU核的利用率永遠是100%,單個線程即可達到單核心的最高吞吐量。

回調函數的陷阱

使用回調函數的方法,很容易寫出這樣的代碼:

function func(callback) {
  fs.readFile('1.txt', 'utf-8', function (err, contents1) {
    if (err) return callback(err);
    fs.readFile('2.txt', 'utf-8', function (err, contents2) {
      if (err) return callback(err);
      fs.writeFile('3.txt', contents1 + contents2, function (err) {
        if (err) return callback(err);
        callback();
      });
    });
  });
}

Callback Pyramids

更深的嵌套

var addUser = function(uid, callback) {
  mongodb.open(function(err, db) {
    if (err) {callback(err); return;}
    db.collection('users', function(err, collection) {
      if (err) {callback(err); return;}
      collection.ensureIndex("uid", function(err) {
        if (err) {callback(err); return;}
        collection.ensureIndex("username", function(err) {
          if (err) {callback(err); return;}
          collection.findOne({uid: uid}, function(err) {
            if (err) {callback(err); return;}
            if (doc) {
              callback(new Error('occupied'));
            } else {
              collection.insert({uid: uid}, function(err) {
                callback(err);
              });
            }
          });
        });
      });
    });
  });
};

Continuation Passing Style

這種通過回調函數進行顯式的控制流傳遞的代碼稱爲「Continuation Passing Style (CPS)

CPS代碼通常使用嵌套的回調函數來組織,線性的邏輯被拆散到不同的回調函數中

然而,用CPS代碼來描述複雜的邏輯對人工來說,難寫、難調、難改

怎麼辦?

Continuation.js

Continuation.js是一個通過編譯的手段,將嵌套的回調函數平面化的工具。 通過它你可以像寫同步I/O的代碼一樣產生CPS的代碼。

$ npm install -g continuation

Continuation.js

通過Continuation.js重寫前面的代碼:

function func(next) {
  try {
    fs.readFile('1.txt', 'utf-8', obtain(contents1);
    fs.readFile('2.txt', 'utf-8', obtain(contents2);
    fs.writeFile('3.txt', contents1 + contents2, obtain());
    next();
  } catch (err) {
    next(err);
  }
}

Continuation.js

使用Continuation.js,你還可以輕易寫出用異步難以描述的邏輯:

var fib = function () {
  var a = 0, current = 1;
  while (true) {
    var b = a;
    a = current;
    current = a + b;
    setTimeout(cont(), 1000);
    console.log(current);
  }
};
fib();

以上代碼是計算Fibonacci數列,每秒輸出一項。

Node.js可以做什麼(第三方模塊)

Node.js可以做什麼(第三方模塊)

The End

謝謝大家
郭家寶

Weibo @byvoid
https://byvoid.com