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
|
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
|
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
|
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
|
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
|
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
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
JavaScript
|
1
2
3
|
exports.add = function(a,b){
return a + b;
};
|
and now in 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.
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.
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
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.
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
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
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
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.
Node.js 开发模式(设计模式)的更多相关文章
- Node.js学习笔记——Node.js开发Web后台服务
一.简介 Node.js 是一个基于Google Chrome V8 引擎的 JavaScript 运行环境.Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又高效.Node.j ...
- Linux虚拟机中 Node.js 开发环境搭建
Node.js 开发环境搭建: 1.下载CentOS镜像文件和VMWare虚拟机程序; 2.安装VMWare——>添加虚拟机——>选择CentOS镜像文件即可默认安装带有桌面的Linux虚 ...
- 《Node.js开发实战详解》学习笔记
<Node.js开发实战详解>学习笔记 ——持续更新中 一.NodeJS设计模式 1 . 单例模式 顾名思义,单例就是保证一个类只有一个实例,实现的方法是,先判断实例是否存在,如果存在则直 ...
- Nodejs学习笔记(一)--- 简介及安装Node.js开发环境
目录 学习资料 简介 安装Node.js npm简介 开发工具 Sublime Node.js开发环境配置 扩展:安装多版本管理器 学习资料 1.深入浅出Node.js http://www.info ...
- Node.js开发Web后台服务
一.简介 Node.js 是一个基于Google Chrome V8 引擎的 JavaScript 运行环境.Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又高效.Node.j ...
- Nodejs学习笔记(一)—简介及安装Node.js开发环境
一.简介 Node.js是让Javascript脱离浏览器运行在服务器的一个平台,不是语言: Node.js采用的Javascript引擎是来自Google Chrome的V8:运行在浏览器外不用考虑 ...
- 【转】Nodejs学习笔记(一)--- 简介及安装Node.js开发环境
目录 学习资料 简介 安装Node.js npm简介 开发工具 Sublime Node.js开发环境配置 扩展:安装多版本管理器 学习资料 1.深入浅出Node.js http://www.info ...
- Node.js开发Web后台服务(转载)
原文:http://www.cnblogs.com/best/p/6204116.html 目录 一.简介 二.搭建Node.js开发环境 2.1.安装Node.js 2.2.安装IDE开发Node. ...
- heX——基于 HTML5 和 Node.JS 开发桌面应用
heX 是网易有道团队的一个开源项目,允许你采用前端技术(HTML,CSS,JavaScript)开发桌面应用软件的跨平台解决方案.heX 是你开发桌面应用的一种新的选择,意在解决传统桌面应用开发中繁 ...
随机推荐
- BZOJ 3546 Life of the Party (二分图匹配-最大流)
题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3546 题意:给定一个二分图.(AB两个集合的点为n,m),边有K个.问去掉哪些点后 ...
- 【转载】C++知识库内容精选 尽览所有核心技术点
原文:C++知识库内容精选 尽览所有核心技术点 C++知识库全新发布. 该知识库由C++领域专家.CSDN知名博客专家.资深程序员和项目经理安晓辉(@foruok)绘制C++知识图谱,@wangshu ...
- XML学习笔记(二)-- DTD格式规范
标签(空格分隔): 学习笔记 XML的一个主要目的是允许应用程序之间自由交换结构化的数据,因此要求XML文档具有一致的结构.业务逻辑和规则.可以定义一种模式来定义XML文档的结构,并借此验证XML文档 ...
- Serialize----序列化django对象
django的序列化框架提供了一个把django对象转换成其他格式的机制,通常这些其他的格式都是基于文本的并且用于通过一个管道发送django对象,但一个序列器是可能处理任何一个格式的(基于文本或者不 ...
- [HDOJ5543]Pick The Sticks(DP,01背包)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5543 题意:往长为L的线段上覆盖线段,要求:要么这些线段都在L的线段上,要么有不超过自身长度一半的部分 ...
- JAVA排序--[选择排序]
package com.array; public class Sort_Select { /** * 项目名称:选择排序 ; * 项目要求:用JAVA对数组进行排序,并运用选择排序算法; * 作者: ...
- HDU 1005 Number Sequence(数列)
HDU 1005 Number Sequence(数列) Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Jav ...
- git学习笔记02-创建一个仓库提交一个文件-原来就是这么简单
打开安装好的git bash,设置你的git信息 (这个随便写就行) 初始化一个Git仓库,分三步.(创建文件夹也可以手动创建,也可以命令行创建) 第一步,进到一个目录 cd e: 第二步,创建一 ...
- centos中更换jdk的版本
现在讲的是Linux中更换jdk版本的问题,卸载Linux自带的jdk更换sun的jdk百度一大堆,但是如果我安装的sun的jdk是1.7的想更换到1.8的如何解决呢,方法其实超easy. 把1.8的 ...
- DOS命令解释程序的编写
实验一.DOS命令解释程序的编写 专业:物联网工程 姓名:黄淼 学号:201306104145 一. 实验目的 (1)认识DOS: (2)掌握命令解释程序的原理: (3)掌握简单的DOS调用方法 ...