Node.js flow control

1,873 views

Published on

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

Published in: Technology
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,873
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
49
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Node.js flow control

  1. 1. Node.js Flow Control MiCloud - Simon
  2. 2. 首先,今天 都是程式碼....
  3. 3. ● node.js ● text editor ● command line tool ● coffee… (可以清醒一點>.<) 請先準備好你的環境...
  4. 4. ● modules? ● callback? ● closure? ● json? 測試一下你對Node.js的認識
  5. 5. Modules [test.js] var say = require(./say’); say.hello(‘simon’) [say.js] exports.hello = function(word) { console.log( ‘Hello ’ + word); }
  6. 6. 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); })
  7. 7. 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);
  8. 8. 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;
  9. 9. 大綱 ● Write a function with callback ● 基本的流程控制作法 ○ array queue流程控制 ○ process.nextTick() + callee ● 流程控制套件介紹 ○ step ○ q ○ node-promise ● Web流程控制 ○ expressjs
  10. 10. Write a function with callback var 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...
  11. 11. 基本的流程控制作法
  12. 12. 插播一下: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
  13. 13. 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當做是攻 擊...
  14. 14. 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...
  15. 15. 插播一下:callee/caller
  16. 16. 解法(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 需要先指定給另一個變 數,後面才能取用...
  17. 17. 3’rd party modules Step, q, node-promise
  18. 18. ● Github: http://github.com/creationix/step.git Step模組
  19. 19. 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); } );
  20. 20. 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); }) }
  21. 21. 其他功能 - 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); } )
  22. 22. 其他功能 - 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); } );
  23. 23. Step注意事項 ● Step的操作是擷取callback作為下一個 function的input ● 如果沒有callback的step,必須要return ● 中間step(ex: step2)若沒有return,則程式有可 能會卡住不動(ex: web app) ● 每個step function中的return不代表中斷整個 流程
  24. 24. 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
  25. 25. q模組 ● Github: https://github.com/kriskowal/q ● 範例: https://github.com/kriskowal/q/wiki/Examples-Gallery
  26. 26. 安裝q模組 npm install q --save
  27. 27. q提供的功能 ● Q.delay ● Q.defer ● Q.nfcall ● Q.nfapply ● Q.ninvoke ● Q.npost ● ...
  28. 28. 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只 保 證 開 始 順 序
  29. 29. 延續剛剛的範例 …(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來接收執 行結果值
  30. 30. node-promise模組 ● Github: https://github.com/kriszyp/node-promise
  31. 31. 安裝node-promise模組 npm install node-promise --save
  32. 32. 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的回傳值
  33. 33. 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
  34. 34. 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存續區域
  35. 35. Web Flow Control expressjs
  36. 36. ExpressJS中的流程控制 ● next()
  37. 37. 透過流程控制增加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);
  38. 38. Reference ● http://opennodes.arecord.us ● http://calculist.org/blog/2011/12/14/why- coroutines-wont-work-on-the-web/ ● http://wiki.commonjs.org/wiki/Promises/A ● https://github.com/basicallydan/q- examples/blob/master/wait-for-multiple- promises.js ● https://github.com/kriskowal/q/wiki/API- Reference
  39. 39. End...

×