39
Node.js Flow Control MiCloud - Simon

Node.js flow control

Embed Size (px)

DESCRIPTION

淺談Node.js中流程控制的概念與相關套件

Citation preview

Page 1: Node.js flow control

Node.js Flow ControlMiCloud - Simon

Page 2: Node.js flow control

首先,今天

都是程式碼....

Page 3: Node.js flow control

● node.js● text editor● command line tool● coffee… (可以清醒一點>.<)

請先準備好你的環境...

Page 4: Node.js flow control

● modules?● callback?● closure?● json?

測試一下你對Node.js的認識

Page 5: Node.js flow control

Modules

[test.js]

var say = require(./say’);say.hello(‘simon’)

[say.js]

exports.hello = function(word) { console.log( ‘Hello ’ + word);}

Page 6: Node.js flow control

Callback

[say.js]

exports.hello = function(word, fn) { fn(‘Hello ’ + word);}

[test.js]var say = require(./say’);say.hello(‘simon’, function(word){

console.log(word);})

Page 7: Node.js flow control

Closure

[say.js]

var f = function(x,y){ return x + y; }

function foo(callback) { var a = 1; var b = 2; callback(a,b); }

foo(f);

Page 8: Node.js flow control

JSON

[test.js]

var a = {name:’simon’};a.sex = ‘man’;a.say = function(word) { console.log(‘Hello’ + word);}

console.log(a.name);a.say(a.name);delete a.sex;

Page 9: Node.js flow control

大綱

● Write a function with callback● 基本的流程控制作法

○ array queue流程控制○ process.nextTick() + callee

● 流程控制套件介紹○ step○ q○ node-promise

● Web流程控制○ expressjs

Page 10: Node.js flow control

Write a function with callbackvar request = require('request') , util = require('util');var url_ds = 'http://odf.micloud.tw/odf/datasets';var url_field = 'http://odf.micloud.tw/odf/%s/field';request.get(url_ds, function(err, res, body){ if(err) console.log(err); var data = JSON.parse(body); for(var i = 0 ; i < data.length ; i++){ var ds = data[i]; var url = util.format(url_field, ds); request.get(url, function(e,r,b){ if(e) console.log(e); console.log('Dataset: %s', ds); console.log(b); }); }});

沒有做好流程控制,你會有更多Callback...

Page 11: Node.js flow control

基本的流程控制作法

Page 12: Node.js flow control

插播一下:for 與 .forEach的差別

var arr = [0,1,2,3,4,5,6,7,8,9];

arr.forEach(function(v){ if(v == 3) return; console.log(v);})

var arr = [0,1,2,3,4,5,6,7,8,9];

for(var i = 0; i< arr.length ; i++){ var v = arr[i]; if(v == 3) return; console.log(v);}

結果是:0,1,2 結果是:0,1,2,4,5,6,7,8,9

Page 13: Node.js flow control

var request = require('request');var queue = [ 'http://www.google.com', 'http://micloud.tw/ch/', 'http://tw.yahoo.com'];queue.forEach(function(v){ var t0 = new Date().getTime(); request({ url: v, method: 'GET' }, function(e,r,d) { var t1 = new Date().getTime(); if(e) console.log(e); console.log('[%s][%s]%s', v, t1-t0, d.substring(0,50)); });});

Case - 網路爬蟲範例

reqeust會在瞬間併發,有可能會被server當做是攻擊...

Page 14: Node.js flow control

var request = require('request');var queue = ['http://www.google.com','http://micloud.tw','http://tw.yahoo.com'];function main(){ var t0 = new Date().getTime(); var _this = queue.pop(); request({url: _this,method: 'GET'}, function(e, r, d){ if(e) console.log(e); var t1 = new Date().getTime(); console.log('[%s][%s]%s', _this, t1-t0, d.substring(0,50)); if(queue.length > 0) { main(); } });}main();

解法(1):自己Call自己的Loop

如果queue還有值,才會call下一個request...

Page 15: Node.js flow control

插播一下:callee/caller

Page 16: Node.js flow control

解法(2):process.nextTick + callee

var request = require('request');var queue = ['http://www.google.com','http://micloud.tw','http://tw.yahoo.com'];

process.nextTick(function fn1(){ var url = queue.pop(); console.log('Processing %s...', url); var _callee = arguments.callee; request.get(url, function(e,r,d){ if(e) console.log(e); console.log('[%s] word count: %s', url, d.length); if(queue.length > 0) process.nextTick(_callee); });});

如果queue還有值,才會call下一個request...

因為Scope的關係,callee需要先指定給另一個變數,後面才能取用 ...

Page 17: Node.js flow control

3’rd party modulesStep, q, node-promise

Page 18: Node.js flow control

● Github: http://github.com/creationix/step.git

Step模組

Page 19: Node.js flow control

Step的用法

var Step = require('step');

Step( function step1() { console.log('Step1...'); throw 'error..'; //這個會掉到step2的arguments[0] return 123; //有return才會往下走

}, function step2() { console.log('Step2...'); console.log(arguments); //可以觀察接到的參數

return 223; }, function step3() { console.log('Step3...'); console.log(arguments); });

Page 20: Node.js flow control

var request = require('request');var queue = ['http://www.google.com', 'http://micloud.tw','http://tw.yahoo.com'];var Step = require('step');Step( function step1() { console.log('Step1...'); getUrl(queue[0], this); }, function step2() { console.log('Step2...'); getUrl(queue[1], this); }, function step3() { console.log('Step3...'); getUrl(queue[2], this); });

使用Step操作爬蟲

function getUrl(url, callback) { //console.log('Processing url:%s...', url); request.get(url, function(e,r,d){ if(e) console.log(e); console.log('[%s] word count:%s', url, d.length); callback(e,d.length); })}

Page 21: Node.js flow control

其他功能 - parallel()

Step( // Loads two files in parallel function loadStuff() { fs.readFile(__filename, this.parallel()); fs.readFile("/etc/passwd", this.parallel()); }, // Show the result when done function showStuff(err, code, users) { if (err) throw err; console.log(code); console.log(users); })

Page 22: Node.js flow control

其他功能 - group()

Step( function readDir() { fs.readdir(__dirname, this); }, function readFiles(err, results) { if (err) throw err; var group = this.group(); // Create a new group results.forEach(function (filename) { fs.readFile(__dirname + "/" + filename, 'utf8', group()); }); }, function showAll(err , files) { if (err) throw err; console.dir(files); });

Page 23: Node.js flow control

Step注意事項

● Step的操作是擷取callback作為下一個function的input

● 如果沒有callback的step,必須要return● 中間step(ex: step2)若沒有return,則程式有可

能會卡住不動(ex: web app)● 每個step function中的return不代表中斷整個

流程

Page 24: Node.js flow control

CommonJS - Promises/A

■ Q Works in both NodeJS and browsers, compatible with jQuery, thenable, and usable as remote objects via message passing

■ RSVP.js A lightweight library that provides tools for organizing asynchronous code

■ when.js Compact promises with joining and chaining

■ node-promise Promises for NodeJS■ jQuery 1.5 is said to be 'based on the

CommonJS Promises/A design'.■ ForbesLindesay/promises-a A bare bones

implementation of Promises/A intended to pass https://github.com/domenic/promise-tests while being as small as possible

■ WinJS / Windows 8 / Metro

Page 25: Node.js flow control

q模組

● Github: https://github.com/kriskowal/q● 範例: https://github.com/kriskowal/q/wiki/Examples-Gallery

Page 26: Node.js flow control

安裝q模組

npm install q --save

Page 27: Node.js flow control

q提供的功能

● Q.delay● Q.defer● Q.nfcall● Q.nfapply● Q.ninvoke● Q.npost● ...

Page 28: Node.js flow control

var Q = require('q') , request = require('request') , queue = ['http://www.google.com','http://micloud.tw','http://tw.yahoo.com'];var fn = function(url) { var deferred = Q.defer(); request.get(url, function(e,r,d){ console.log('[%s] word count:%s', url, d.length); deferred.resolve(); }); return deferred.promise;};Q.allResolved( [ fn(queue[0]), fn(queue[1]), fn(queue[2]) ] ) .then(function(){ console.log(‘end...’) }).done();

使用q操做爬蟲

q只保證開始順序

Page 29: Node.js flow control

延續剛剛的範例

…(skip)var out = function (x, y, z) { var d = Q.defer(); console.log('x:%s, y:%s, z:%s', x, y, z); d.resolve(); return d.promise;};

Q.allResolved([fn(queue[0]), fn(queue[1]), fn(queue[2])]) .spread(out) .then(function(){ console.log('end...'); }) .done();

使用spread來接收執

行結果值

Page 30: Node.js flow control

node-promise模組

● Github: https://github.com/kriszyp/node-promise

Page 31: Node.js flow control

安裝node-promise模組

npm install node-promise --save

Page 32: Node.js flow control

var Promise = require("node-promise").Promise , request = require('request');var p = new Promise();function step1(){ request.get("http://www.google.com", function(e,r,d){ console.log('>>1'); p.resolve(d); });}step1();p.then(function(d){ console.log('>>2'); console.log('word count:%s', d.length); }, function(err){ console.log(err); })

使用node-promise操做爬蟲

可以透過then來取回

resolve的回傳值

Page 33: Node.js flow control

var Promise = require("node-promise").Promise , request = require('request')var queue = ["http://www.google.com", "http://micloud.tw", "http://tw.yahoo.com"];var p;function step1(url){ p = new Promise(); request({ url : url, method : "GET" }, function(e,r,d){ console.log('>>url:%s', url); p.resolve(d); });}step1(queue[0]);step1(queue[1]);

錯誤的操作範例

因為promise(p)的scope問

題,會導致runtime

exception

Page 34: Node.js flow control

var Promise = require("node-promise").Promise , request = require('request') , util = require('util')var site = ‘http://odf.micloud.tw’var url = site + '/odf/datasets'var url_detail = site + '/odf/%s/field';function step1(url){ var p = new Promise(); request({ url : url, method : "GET" }, function(e,r,d){ p.resolve(JSON.parse(d)); }); return p;}

延續剛剛的範例

step1(url).then(function(d){ for(var i = 0 ; i< d.length ; i++){ step1(util.format(url_detail, d[i])) .then(function(d){ console.log(d); }); }})

使用return promise的方式,串連then操作

使用function scope限制promise存續區域

Page 35: Node.js flow control

Web Flow Controlexpressjs

Page 36: Node.js flow control

ExpressJS中的流程控制

● next()

Page 37: Node.js flow control

透過流程控制增加authfilter

//增加req.headers.auth認證

function authfilter(req, res, next){ if(req.headers.auth == 'demo') next(); //next代表通過此filter else //認證錯誤,則回傳statusCode與錯誤訊息

res.send(401, 'Auth Error!'); }

//則在routing中可以安插在真正執行route之前

app.get('/users', authfilter, user.list);

Page 39: Node.js flow control

End...