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.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏绿巨人专栏

Database Design Guidelines

2477
来自专栏JetpropelledSnake

SQL学习之简单增删改查

1766
来自专栏函数式编程语言及工具

PICE(3):CassandraStreaming - gRPC-CQL Service

  在上一篇博文里我们介绍了通过gRPC实现JDBC数据库的streaming,这篇我们介绍关于cassandra的streaming实现方式。如果我们需要从一...

1450
来自专栏MasiMaro 的技术博文

遍历系统中加载的驱动程序以及通过设备对象指针获取设备对象名称

遍历系统中加载的驱动可以在R3层完成,通过几个未导出的函数:ZwOpenDirectoryObject、ZwQueryDirectoryObject,下面是具体...

872
来自专栏编码小白

ofbiz实体引擎(八) 创建表

/** * @author 郑小康 * * 1.检验实体是否为空 * * 2.检验视图实体是否为空 ...

2583
来自专栏码匠的流水账

聊聊reactor-netty的PoolResources的两种模式

本文主要研究下reactor-netty的PoolResources的两种模式elastic及fixed。

551
来自专栏张善友的专栏

弹出式模态窗体选择文本控件

2006年就要到来了,最近比较忙,很少更新blog,今天发一个模态窗体选择文本控件辞旧迎新.新年在发几个asp.net2.0 webPart控件同各位分享: ...

1867
来自专栏数据结构与算法

Day3上午解题报告

预计分数:100+40+50=190 实际分数:100+40+50=190 T1 https://www.luogu.org/problem/show?pid=...

2645
来自专栏海天一树

小朋友学Java(13):控制台输入

C语言用scanf来输入,C++用cin来输入,java则用Scanner来输入。 程序 import java.util.*; public class Sc...

2656
来自专栏绿巨人专栏

Database Design Guidelines

994

扫码关注云+社区