很多node入门的书里面都会在介绍node特性的时候说:单线程,异步式I/O,事件驱动。

Node不是一门语言,它是运行在服务器端的开发平台,官方指定语言为javascript。

阻塞和线程:

线程在执行中如果遇到磁盘读写或网络通信(统称为 I/O 操作),通常要耗费较长的时间,这时操作系统会剥夺这个线程的 CPU 控制权,使其暂停执行,全力执行这个I/O操作,同时将资源让给其他的工作线程,这种线程调度方式称为阻塞。当其他完成之后,系统再恢复它对cpu的控制权,继续执行,这就是同步I/O或者阻塞式I/O。

所以这个模式之下,一个线程只能处理一个任务,要么是计算操作,要么是I/O操作等等。每当有多的请求发过来的时候,必须多加线程用来响应。

同样的,在异步式或者非阻塞式,系统对所有的I/O操作部阻塞,而是将这个耗费时间和资源的操作报告给OS,就继续执行下一条语句。当OS执行完毕这个I/O操作之后,以事件的形式通知原来请求挂载I/O操作的线程,线程会在特定的时间处理这个事情。所以,必线程必须有事件循环,不断检查有没有未处理的事件。

所以这个模式下,cpu的核心利用率永远是100%,I/O以事件形式通知。

总结:多线程同步式I/O阻塞模式通过加开线程响应更多的请求,好处是在多核cpu的情况下利用更多的核。

单线程模式异步式I/O非阻塞式一个线程永远在执行计算操作,这个线程使得cpu的核心利用率为100%。通过功能划分利用多核CPU。

这不是殊途同归吗?node采用后者的原因是什么呢?

单线程的牛逼之处在于不用创建更多的线程,也就是可以节省掉创建线程所浪费的资源。理论依据是加开一个新线程是非常耗费资源的。

关于异步式I/O(磁盘读写或网络通信)和事件驱动:

Node维护一个事件队列。

普通方式查询数据库操作:

res=db.query(‘select * from *’);

res.output();

node解决方案:

db.query(‘select * from *’, function (res){

res.output();

});

上面用到回调函数。实现非阻塞的方式请求。

弊端:一个完整的逻辑拆分为一个个事件,增加开发调试的难度;解决方案在后面提及。

两个通过node读取文件的例子fs.readFile api(异步式(回调函数来实现)和同步式):

var fs = require('fs');

fs.readFile('file.txt', 'utf-8', function(err, data) {

if (err) {

console.error(err);

} else {

console.log(data);

}

});

console.log('end.');

var fs = require('fs');

var data = fs.readFileSync('file.txt', 'utf-8');

console.log(data);

console.log('end.');

两者输出数据的顺序不同。但是功能上没有什么区别。

关于模块:

node提供了exports和require两个对象。exports是模块公开的接口,require是用于获取这个模块的接口。

实例:

创建两个文件,一个当做外面的模块进行加载module.js,另一个是程序的入口文件getmodule.js.

module.js:

var name;

exports.setname=function(name){

name=name;

};

exports.sayhello(){

console.log(‘hello’+name);

};

getmodule.js:

var test=require(‘./module.js’);

test.setname(‘zhou’);

test.sayhello();

node getmodule.js

hellozhou

这就实现了接口的封装。module.js通过exports对象吧两个函数作为间接接口,在getmodule.js中通过require加载一个模块,之后就可以直接访问module中的exports对象的成员函数。

创建包

包是模块基础上更深一步的抽象。

下面,可以将一个文件夹somepckage封装成一个模块。这个文件里里面要有一个index.js的文件,像module.js一样。然后在getmodule.js的文件里面,可以直接用var a=require(./package);

之后就可以通过a.xxxx来访问index.js里面的函数了。亲测可行。

题外话:关于全局安装依赖包和选择目录安装依赖包的优缺点。

全局的好处是可以提高程序重复利用的程度。避免同样的内容存在于多个副本。坏处是难以处理不同的版本依赖。

本地的好处是不会有不同程序依赖不同版本包的问题。同时减轻了包作者的API兼容性压力,但是缺陷是要一个个安装,非常繁琐。

node有全局和本地两种方式选择。

我们选择全局安装的理由有:本地安装不会注册path环境变量。例如在一个工程下安装的supervisor不会再另一个工程中发挥作用。

但是,使用全局安装下的包不可以通过require访问,这是一个悲伤的事。本地安装的可以通过require访问,但是不注册path环境变量;全局安装的不可以通过require访问,但是注册path环境变量。

总而言之,当我们要把某个包作为工程运行时的一部分时,通过本地模式获取,如果要

在命令行下使用,则使用全局模式安装。

还有就是怎样发布自己的npm包供全世界的人使用。

下面介绍node的核心模块。(全局变量,常用工具,事件机制,文件系统,http服务器和客户端)

全局变量

全局对象:可以在程序的任何部分访问的对象就是全局变量,类似于上面所说的全局安装。增加了path环境变量。

全局对象及其所有的属性就是全局变量,可以任意访问。

在浏览器javascript中,window就是全局变量,我们可以在任何地方使用window.open……

node中,全局对象是global。所有全局变量,都是global的属性。包括console,process等。

process:(全局变量,global的一个属性)

作用:用于描述当前node进程的对象,提供了一个与操作系统的简单接口。

process.argv:命令行参数数组,可以返回命令行的参数为一个数组,数组第一个元素为node,第二个为文件目录,以后为运行参数;

process.stdout:标准输出流,process.stdout.write()比console.log();更加接近底层;

process.stdin:标准输入流;

process.nextTick(callback):为事件循环设置一项任务,node会在下次事件循环相应的时候调用callback。比setTimeout(fn,0)更加高效。

console:用于提供控制台标准输出

console.log():

console.log ('Hello world');

console.log('byvoid%diovyb');

console.log('byvoid%diovyb', 1991);

输出:

Hello world

byvoid%diovyb

byvoid1991iovyb

console.error():向标准的错误流输出

console.trace():向标准错误流输出当前的调用栈。

常用工具

var util = require('util');

util.inherits

util.inherits(constructor, superConstructor)是一个实现对象间原型继承的函数。

util.inherits(Sub, Base);

sub继承自base;

util.inspect

util.inspect(object,[showHidden],[depth],[colors])是一个将任意对象转换为字符串的方法,通常用于调试和错误输出。它至少接受一个参数 object,即要转换的对象。

depth:最大递归层数,默认两层,null不限次数直到遍历完成。

事件驱动模块events

 

events是node最重要的模块。被几乎所有的模块依赖。

事件发射器

events模块只提供了一个对象:events.EventEmitter。核心就是事件发射与事件监听器的封装。

var events = require('events');

var emitter = new events.EventEmitter();

相关API:

emitter.on(event,listener):为enent事件注册一个监听器,接受字符串event和一个回调函数listener;

emitter.emit(event,[arg1],[arg2],[arg3],[arg4])发射event事件,传递若干可选参数到事件监听器的参数表;emitter.emit('error');当 error 被发射时,EventEmitter 规定如果没有响应的监听器,Node.js 会把它当作异常,退出程序并打印调用栈。

EventEmitter.once(event, listener) 为指定事件注册一个单次监听器,即监听器最多只会触发一次,触发后立刻解除该监听器。

EventEmitter.removeListener(event, listener) 移除指定事件的某个监听器,listener 必须是该事件已经注册过的监听器。

EventEmitter.removeAllListeners([event]) 移除所有事件的所有监听器,如果指定 event,则移除指定事件的所有监听器。

相当抽象,来个例子好了:

var events = require('events');

var emitter = new events.EventEmitter();

emitter.on('someEvent', function(arg1, arg2) {//为someEvent事件注册了一个监听器——一个匿名回调函数。

console.log('listener1', arg1, arg2);

});

emitter.on('someEvent', function(arg1, arg2) {/为someEvent事件又注册了一个监听器——一个匿名回调函数。

console.log('listener2', arg1, arg2);

});

emitter.emit('someEvent', 'byvoid', 1991);//发射enent事件,传递两个参数到事件监听器的参数表

结果:

listener1 byvoid 1991

listener2 byvoid 1991

总结:emitter使用.on为someEvent事件注册了两个事件监听器,然后使用.enit发射事件someEvent,并传递两个参数给事件监听器。于是这两个事件监听器回调函数先后被调用。

文件操作模块(fs):

node给文件操作同时提供了异步和同步的两种方式。

异步方式:

fs.readFile(filename,[encoding],[callback(err,data)])

文件名,编码方式(缺省为二进制),回调函数。data为文件内容。

var fs = require('fs');

fs.readFile('content.txt', function(err, data) {

if (err) {

console.error(err);

} else {

console.log(data);

}

});

输出(假设源文件是utf-8编码):

<Buffer 54 65 78 74 20 e6 96 87 e6 9c ac e6 96 87 e4 bb b6 e7 a4 ba e4 be 8b>

指定编码方式:

var fs = require('fs');

fs.readFile('content.txt', 'utf-8', function(err, data) {

if (err) {

console.error(err);//如果运行出错,err将是Error的对象。

} else {

console.log(data);

}

});

输出:
Text 文本文件示例

关于回调函数的补充:

Node.js 的异步编程接口习惯是以函数的最后一个参数为回调函数,通常一个函数只有一个回调函数。回调函数是实际参数中第一个是 err,其余的参数是其他返回的内容。如果没有发生错误,err 的值会是 null undefined。如果有错误发生,err 通常是 Error 对象的实例。

 

同步方式:

fs.readFileSync(filename,
[encoding])。

fs.open

fs.open(path,
flags, [mode], [callback(err, fd)])

路径,flag是确认文件的打开方式(读写,只读只写,创建与否),mode默认0666,回调函数传回一个文件描述符fd。

fs.read

fs.read(fd,
buffer, offset, length, position, [callback(err, bytesRead,buffer)])

比fs.readFile 提供了更底层的接口。

(文件描述符,写入buffer指向,buffer写入偏移量,读取字节数,读取起始位置,回调函数传递两个参数——字节数和缓冲区对象)

参考资料:《node.js开发指南》

(未完待续,欢迎指出错误)

Node.js学习笔记(4)——除了HTTP(服务器和客户端)部分的更多相关文章

  1. Node.js学习笔记 01 搭建静态服务器

    希望这篇文章能解决你这样一个问题:“我现在已经了解了一些Node.Js基本概念了,怎么搭一台静态服务器呢?” 请参考一下博主的前两篇文章: 完全面向于初学者的Node.js指南 Node.Js的Mod ...

  2. 一点感悟:《Node.js学习笔记》star数突破1000+

    写作背景 笔者前年开始撰写的<Node.js学习笔记> github star 数突破了1000,算是个里程碑吧. 从第一次提交(2016.11.03)到现在,1年半过去了.突然有些感慨, ...

  3. Node.js学习笔记(2):基本模块

    Node.js学习笔记(2):基本模块 模块 引入模块 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在No ...

  4. Node.js学习笔记(1):Node.js快速开始

    Node.js学习笔记(1):Node.js快速开始 Node.js的安装 下载 官方网址:https://nodejs.org/en/ 说明: 在Windows上安装时务必选择全部组件,包括勾选Ad ...

  5. Node.js学习笔记(3):NPM简明教程

    Node.js学习笔记(3):NPM简明教程 NPM常用操作 更新NPM版本 npm install npm -g -g,表示全局安装.我们可以指定更新版本,只需要在后面填上@版本号即可,也可以输入@ ...

  6. 系列文章--Node.js学习笔记系列

    Node.js学习笔记系列总索引 Nodejs学习笔记(一)--- 简介及安装Node.js开发环境 Nodejs学习笔记(二)--- 事件模块 Nodejs学习笔记(三)--- 模块 Nodejs学 ...

  7. Node.js学习笔记(4):Yarn简明教程

    Node.js学习笔记(4):Yarn简明教程. 引入Yarn NPM是常用的包管理工具,现在我们引入是新一代的包管理工具Yarn.其具有快速.安全.可靠的特点. 安装方式 使用npm工具安装yarn ...

  8. Node.js学习笔记(一)基础介绍

    什么是Node.js 官网介绍: Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js us ...

  9. Node.js学习笔记(2) - Node.js安装及入门hello world

    今天来简单的记录一下Node.js的安装配置以及简单的入门 一.Node.js的安装 1.windows下的安装 windows下的安装很简单,只需要去官网http://nodejs.org中,找到w ...

  10. Node.js学习笔记(六) --- Nodejs 的非阻塞 I/O、 异步、 事件驱动

    1. Nodejs 的单线程 非阻塞 I/O 事件驱动在 Java. PHP 或者.net 等服务器端语言中,会为每一个客户端连接创建一个新的线程.而每个线程需要耗费大约 2MB 内存.也就是说,理论 ...

随机推荐

  1. [BZOJ]5018: [Snoi2017]英雄联盟 DP

    [Snoi2017]英雄联盟 Time Limit: 15 Sec  Memory Limit: 512 MBSubmit: 270  Solved: 139[Submit][Status][Disc ...

  2. Android jni 编程入门

    本文将介绍如何使用eclipse和ndk-build来编写一个基于Android4.4版本的包含有.so动态库的安卓程序. 前提是已经安装和配置好了诸如SDK,NDK等编译环境.下面开始编程! 1 程 ...

  3. poj 2441 Arrange the Bulls

    Arrange the Bulls Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 5427   Accepted: 2069 ...

  4. pat 甲级 Cars on Campus (30)

    Cars on Campus (30) 时间限制 1000 ms 内存限制 65536 KB 代码长度限制 100 KB 判断程序 Standard  题目描述 Zhejiang University ...

  5. 【HDOJ5533】Dancing Stars on Me(计算几何)

    题意:给定二维平面上的n个整点,问它们是否都在正n边形的定点上 n<=100,abs(x[i]),abs(y[i])<=1e4 思路:队友做的,抱大腿 可以发现只有n=4时顶点有可能都是整 ...

  6. aspx生成静态页面html 例子

    原文发布时间为:2009-07-26 -- 来源于本人的百度文章 [由搬家工具导入] using System;using System.Data;using System.Configuration ...

  7. ubuntu php编译安装配置

    安装参考:http://ilanni.blog.51cto.com/526870/1569322/ 加载扩展的一些参数参考:http://java-er.com/blog/nginx-php-fpm/

  8. Android开发跳坑记录

    本文主要记录在Android开发中遇见的一些问题,以及解决方法. 2015.12.01 1.adb.exe 端口被占用 解决: http://blog.csdn.net/xiaanming/artic ...

  9. 模拟浏览器的GET和POST动作

    Jakarta的httpclient3.1是最新版本,项目中需要用程序模拟浏览器的GET和POST动作.在使用过程中遇到不少问题.1. 带附件的POST提交    最开始都是使用MultipartPo ...

  10. MSP430 G2553 寄存器列表与引脚功能

    USCI_B0 USCI_B0 发送缓冲器UCB0TXBUF 06Fh USCI_B0 接收缓冲器UCB0RXBUF 06Eh USCI_B0 状态UCB0STAT 06Dh USCI B0 I2C ...