Node.js 开发模式(设计模式)

Asynchronous code & Synchronous code

As we have seen in an earlier post (here), how node does things Asynchronously. For a “Traditional programmer”, this can be a tough pill to swallow. So lets take a look at how things can be done async.

Tradition programming

Sync Operation

1

2

3

4

5

6

7

var result = db.query("select * from someTable");

// use the result here and do something

var doSomething  = function(){

// doing something totally unrelated to the db call.

};

doSomething(); // This will be blocked till the db response has arrived.

Here the  doSomething() executes a set of statements that are not dependent on the response of the db call. But it has to wait till the db operation is completed. This is why we have a server like Node to take care of all the I/O threads separately.

So in an async world, the same will be written like this

Async

1

2

3

4

5

6

7

8

9

var result = db.query("select * from someTable", function(){

// use the result here and do something

});

var doSomething  = function(){

// doing something totally unrelated to the db call.

};

doSomething(); // The DB request gets fired and doSomething() will get executed after that.

Here, the DB request gets fired and doSomething()  will get executed immediately after that. All the actions happen async. Its Node’s event loop’s responsibility to take care of I/O operations and fire the registered callback.

Now, life is always not that simple is it? (tell me about it!..) Take a look at this example

Complex Async

1

2

3

4

5

6

var result = db.query("select * from someTable", function(data){

var userId = data.get(1).id;

var subQResult = db.query("select * from someOtherTable where id="+userId+";", function(userData){

// some more info.. and the fire another db call!

});

});

or

Complex file reader

1

2

3

4

5

6

7

var file = fs.readFile("/usr/bin", function(data){

var listOfFiles = data.getContents();

var anotherFile = fs.readFile(listOfFiles[0], function(userIDs){

var user = userIds[0];

// Call db and get the data for this user

});

});

Very nested and convoluted? So can we fix this whole nested thing? Yes, you can use any of the following modules

So our code will turn into

Flowjs - Async Simplification

1

2

3

4

5

6

7

8

9

10

11

12

13

flow.exec(

function() {

readFile("/usr/bin", this);

// process data

},function(url) {

// get some other file

},function(userIDs) {

var user = userIds[0];

// and so on

},function() {

completedAll()

}

);

Now, lets take a look at Node’s Modules.

Node Modules

If you have interacted with programming languages like C, C++, Java, .Net or PHP, you would have seen statements like import  using  #include  include or  require to get external files/libraries to your current file. Since the code is isolated in these programming languages, we need to explicitly include the required libraries.

But, Javascript runs everything in the global scope and does not have a partition between variables/functions or variables/functions of a file. In simple, there is no namespacing!.

If you have 3 files, fileOne.js , fileTwo.js  & fileThree.js  and you loaded them in your browser in the same order, The function definition or variable values of the prior will be overridden by the later without any warnings.

Lets say fileOne has a method called add(num1,num2);  which adds two numbers & fileTwo has a method called add(str1, str2);  which concats’ two strings. And fileThree is calling theadd(5,4);  expecting the method to return the sum. But instead, it receives a concatenated string.

This phenomenon in the programming world is called as the “spaghetti code”. Unless you are careful about your variables names, you might override someone else’s code or some one might override yours!

So we need to use a dependency management system, that will take care of things like these. Node uses CommonJs Modules for handling dependency.

CommonJS dependency management revolves around two methods exports  &  require.

Let’s reconsider the above example, and implement the same via CommonJs modules.

fileOne.js

fileOne.js

JavaScript

1

2

3

4

5

6

exports.add = function(a,b){

if(!isNaN(a) && !isNaN(b))

return parseInt(a) + parseInt(b);

else

return "Invalid data";

};

fileTwo.js

fileTwo.js

JavaScript

1

2

3

exports.add = function(a,b){

return a + b;

};

and now in fileThree.js

fileThree.js

JavaScript

1

2

3

4

var moduleOne = require("./fileOne");

var moduleTwo = require("./fileTwo");

console.log(moduleOne.add(5,4)); // This will be the sum for sure!!

Neat right? Node uses this for its dependency management & name-spacing and you need to when you are developing code around Node.

Javascript’s Callback

A call back basically is a function that will get invoked after the initiated task is completed.

That means, I want do something after some other thing is completed. Like, after 5 seconds fire an alert.

SetTimeOut

JavaScript

1

2

3

setTimeOut(function(){

alert("I had to wait for 521ms before I am shown!");

}, 521);

Or in Node, since everything is async, the callback gets fired on completion of an I/O operation.

Node I/O

JavaScript

1

2

3

var results = db.query("select * from someTable", function(data){

// I am a callback!!. I will be fired only after the query operation is completed.

});

Callback function can be named or anonymous.

As we have seen earlier, nesting callbacks can be a nightmare for code readability. We have also seen libraries like async would help clean the code for us. Another way to implement the same without any external module is

Callback Named Functions

JavaScript

1

2

3

4

5

6

7

8

9

10

11

var result = db.query("select * from someTable", processData);

var processData = function(data)

{

var userId = data.get(1).id;

var subQResult = db.query("select * from someOtherTable where id="+userId+";", processSomeMoreData);

};

var processSomeMoreData = function(userData){

// some more info.. and the fire another db call!

};

Any async function in node accepts a callback as it’s last parameter.

So, this is what you can expect from Node.

Node Callback

JavaScript

1

2

3

4

myNodeFunction(arg1, arg2, callback);

myOtherNodeFunction(arg1, arg2, arg3, callback);

function callback(err, result) { ... }

And the callback function’s first argument is always an error object (if there an error, else null) and the second argument is the results from the parent Function.

The Event Emitter Pattern

Let’s take a look at some sample code first

Event Driven

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

var net = require(‘net‘);

var port = 1235;

net.createServer(function(socket) {

console.log(‘A new client connected‘);

socket.on(‘data‘, function(data) {

console.log(‘Data received from client : ‘+data);

});

socket.on(‘close‘, function(data) {

console.log(‘A client disconnected‘);

});

}).listen(port, "localhost");

console.log("Server Running on "+port+".\nLaunch http://localhost:"+port);

The above code is a simple TCP server. Lines 4,7 & 10 register events. And the server gets created. When a client navigates to  http://localhost:1235 the server starts to listen to the new client. And registers events when a data comes in & when a client disconnects from the server.

So when a client connects, we see a console log about the connection. We wait.. wait.. wait.. and then the client emits a data event, the server logs it & finally the client disconnects.

This model is also called as the “PubSub” - A publisher-subscriber. For all you jQuery devs out there, you know this!! (You register an event on a button click and write some code and wait for the button click). Some call this as Observable pattern. You can figure this out here.

So, In simple, our server code will get executed only if there is an action.This is a simple example of event driven development in Node.

Node provides an option to write & trigger custom event too. Take an example of a baseball

Event Emitter

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

var events = require(‘events‘); // require events

var eventEmitter = new events.EventEmitter(); // create a new instance

var homeRun = function()

{

console.log(‘Home Run!!‘);

}

// Register an event for ‘swing‘

eventEmitter.on(‘swing‘,homeRun); // yay!!

// Ball pitched.. So lets emit a ‘swing‘

event eventEmitter.emit(‘swing‘);

What happened here?

  • We created a response ( homeRun())
  • We registered an event (‘ swing’) passing the callback ( homeRun())
  • We Emitted the event (‘ swing’)

Apart from the above way of implementing the eventEmitter, we can inherit the same too. What do I mean? Take a look at this

Baseball Swing

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

var events = require(‘events‘);

function Batter(name) {

this.name = name;

events.EventEmitter.call(this); // making the Batter class a event emitter.

//Invoking the EventEmitter‘s constructor with Batter.

this.swing = function()

{

this.emit(‘swing‘);

}

}

Batter.prototype = events.EventEmitter; // Inheriting EventEmitters methods into Batter. ex: ‘on‘, as user below

var batter = new Batter(‘Babe Ruth‘);

batter.on(‘swing‘, function() {

console.log(‘It is a Strrikkkeee!!!!‘);

});

batter.swing();

Pretty neat right? Now you can have your own class that is invisible.

So these are some of the ways, in which node should be implemented. Depending on our requirement, you can pick from above.

时间: 2024-10-09 23:23:00

Node.js 开发模式(设计模式)的相关文章

Nodejs学习笔记(一)--- 简介及安装Node.js开发环境

目录 学习资料 简介 安装Node.js npm简介 开发工具 Sublime Node.js开发环境配置 扩展:安装多版本管理器 学习资料 1.深入浅出Node.js http://www.infoq.com/cn/minibooks/nodejs 2.Node.js开发指南 简介(只捡了我觉得重要的) Node.js是让Javascript脱离浏览器运行在服务器的一个平台,不是语言: Node.js采用的Javascript引擎是来自Google Chrome的V8:运行在浏览器外不用考虑头

Node.js开发入门—Stream用法详解

Stream是Node.js中非常重要的一个模块,应用广泛.一个流是一个具备了可读.可写或既可读又可写能力的接口,通过这些接口,我们可以和磁盘文件.套接字.HTTP请求来交互,实现数据从一个地方流动到另一个地方的功能. 所有的流都实现了EventEmitter的接口,具备事件能力,通过发射事件来反馈流的状态.比如有错误发生时会发射"error"事件,有数据可被读取时发射"data"事件.这样我们就可以注册监听器来处理某个事件,达到我们的目的. Node.js定义了R

node,js开发环境的搭建

1.node.js开发环境的下载,不过要根据自己使用的电脑和使用的操作系统的实际情况,具体下载地址如下:https://nodejs.org/en/download/2.安装好之后进行测试(1)使用以下命令来查看当前的 Node 版本: $ node -v v4.4.3 (2)同样也可以执行npm -v验证NPM工具是否随Node安装成功. $ npm -v 3.10.103.交互模式打开终端,键入node进入命令交互模式,可以输入一条代码语句后立即执行并显示结果,例如: $ node > co

【转】Nodejs学习笔记(一)--- 简介及安装Node.js开发环境

目录 学习资料 简介 安装Node.js npm简介 开发工具 Sublime Node.js开发环境配置 扩展:安装多版本管理器 学习资料 1.深入浅出Node.js http://www.infoq.com/cn/minibooks/nodejs 2.Node.js开发指南 简介(只捡了我觉得重要的) Node.js是让Javascript脱离浏览器运行在服务器的一个平台,不是语言: Node.js采用的Javascript引擎是来自Google Chrome的V8:运行在浏览器外不用考虑头

用Node.js开发Windows 10物联网应用

(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 未来10年内,物联网将会如移动互联网这样深入到我们生活的各方各面.所以微软现在对物联网进行了大量的投资,比如Windows 10就有一个单独的IoT版本.而今天推荐的文章是告诉大家如何把Node.js开发带到Windows 10 IoT中. 在月初Build大会上,微软发布了Windows 10 IoT Core Insider Preview,这个版本可以安装到树莓派2(Raspberry Pi

Mac下进行Node.js开发环境的配置

我们知道,Mac是一款天生可以提升我们开发效率的系统,合理的使用Mac下面的神器,有助于提升我们的开发效率.个人是个VIM控,今天分享一下,我在mac下是如何进行Node.js的开发环境搭建的.先给大家看看最终的效果图片吧! Iterm2 首先,推荐Iterm2这款命令行神器.它是一款终端替代工具,支持 Macos 10.5+ 版本.它具备很多优点:比如开源免费.快捷键丰富.水平垂直分屏.远程使用vi时兼容性好.如果再加上一点,就是它UI也比较友好,如果你喜欢捣鼓,可以配置主题,让iterm2界

Mac下进行Node.js开发环境配置

我们知道,Mac是一款天生可以提升我们开发效率的系统,合理的使用Mac下面的神器,有助于提升我们的开发效率.个人是个VIM控,今天分享一下,我在mac下是如何进行Node.js的开发环境搭建的.先给大家看看最终的效果图片吧! Iterm2 首先,推荐Iterm2这款命令行神器.它是一款终端替代工具,支持 Macos 10.5+ 版本.它具备很多优点:比如开源免费.快捷键丰富.水平垂直分屏.远程使用vi时兼容性好.如果再加上一点,就是它UI也比较友好,如果你喜欢捣鼓,可以配置主题,让iterm2界

推荐近期15个 Node.js 开发工具

近来Node.js 越来月流行了,这个基于Google V8 引擎建立的平台, 用于方便地搭建响应速度快.易于扩展的网络应用.在本文中,我们列出了2015年最佳的15个 Node.js 开发工具.这些工具对于刚刚开始学习 Node.js 的新手开发者非常有帮助.如果你知道任何其他有用的 Node.js 资源,请让我们知道. 1. IO.js JavaScript的I / O是一个NPM兼容的平台,最初是基于Node.js和建立在Chrome V8运行.它的目的是提供更快和可预测的发布周期. 2.

Node.js开发入门—语音合成示例

出于项目需要,搞了一个语音合成(TTS)的小示例,使用的是OKVoice. 我想在PC上测试,OKVoice的快速接入API可以实现我的目的,文档在这里:http://dev.okvoice.com/file.php. 直接上代码吧,okVoiceTts.js,内容如下: var http = require('http'); var fs =require('fs'); var crypto = require('crypto'); var util = require('util'); va