我的Node.js学习之路(三)--node.js作用、回调、同步和异步代码 以及事件循环


Posted in Javascript onJuly 06, 2014

一,node.js的作用,

I/O的意义,(I/O是输入/输出的简写,如:键盘敲入文本,输入,屏幕上看到文本显示输出。鼠标移动,在屏幕上看到鼠标的移动。终端的输入,和看到的输出。等等)
 
node.js想解决的问题,(处理输入,输入,高并发 。如 在线游戏中可能会有上百万个游戏者,则有上百万的输入等等)(node.js适合的范畴:当应用程序需要在网络上发送和接收数据时Node.js最为适合。这可能是第三方的API,联网设备或者浏览器与服务器之间的实时通信)
 
并发的意义,(并发这个术语描述的是事情会在同时发生并可能相互交互。Node的事件化的I/O模型让我们无需担心互锁和并发这两个在多线程异步I/O中常见的问题)
 
演示网络I/O

Js代码 

var http = require('http'), 
  urls = ['www.baidu.com','www.10jqka.com.cn','www.duokan.com']; 
function fetchPage(url){ 
  var start = new Date(); 
  http.get({host:url},function(res){ 
    console.log("Got response from:" + url); 
    console.log("Request took:",new Date() - start, "ms"); 
  }); 
} 
for(var i=0; i<urls.length; i++){ 
  fetchPage(urls[i]); 
}

命名为,node.js
我们在终端里面运行node node.js
输出:

我的Node.js学习之路(三)--node.js作用、回调、同步和异步代码 以及事件循环

我们要求node.js访问三个url并报告收到响应的情况以及所耗费的时间。
我们可以看到两次输出的时间是不一样的。受各种影响,解析DNS请求的时间,服务器繁忙程序等等。
 
为什么javascript是一个事件驱动的语言
javascript围绕着最初与文档对象模型(DOM)相关的事件架构。开发人员可以在事件发生时做事情。这些事件有用户点击一个元素,页面完成加载等。使用事件,开发人员可以编写事件的监听器,当事件发生时被触发。
 
二,回调(Callback)
1,什么是回调
2,剖析回调
 
回调指的是将一个函数作为参数传递给另一个函数,并且通常在第一个函数完成后被调用。
 
例子:如jquery中的hide()方法,
Js代码 

1,$("p").hide('slow'); 
2,$("p").hide('slow',function(){alert("The paragraph is now hidden")});

回调是可选的,
1就不需要回调
2,是有回调的,当段落隐藏完成后它就会被调用,显示一个alert提示。
 
为了可以看到带与不带回调的代码之间的区别
Js代码 

$("p").hide('slow'); 
alert("The paragraph is now hidden");//1 
 
$("p").hide('slow',function(){alert("The paragraph is now hidden")});//2

1,是没有回调,,执行顺序是一样但是,我们可以看到p段落还没有隐藏完全,alert就出来
2,是有回调的,执行则是hide完成后在alert
 
 剖析回调
Js代码 

function haveBreakfast(food,drink,callback){ 
  console.log('Having barakfast of' + food + ', '+ drink); 
  if(callback && typeof(callback) === "function"){ 
    callback(); 
  } 
} 
 
haveBreakfast('foast','coffee',function(){ 
  console.log('Finished breakfast. Time to go to work!'); 
});

 
输出:

Having barakfast of foast,coffee
Finished breakfast. Time to go to work!

这里是创建了一个函数,有三个参数,第三个参数是callback,这个参数必须是个函数。
haveBreakfast函数将所吃的东西记录到控制台中然后调用作为参数传递给它的回调函数。
 
Node.js如何使用回调

node.js中使用filesystem模块从磁盘上读入文件内容的示例
 
Js代码 

var fs = require('fs'); 
 
fs.readFile('somefile.txt','utf8',function(err,data){ 
  if(err) throw err; 
  console.log(data); 
});

结果是:somefile.txt里面的内容。
1,fs(filesystem)模块被请求,以便在脚本中使用
2,讲文件系统上的文件路径作为第一个参数提供给fs.readFile方法
3,第二个参数是utf8,表示文件的编码
4,将回调函数作为第三个参数提供给fs.readFile方法
5,回调函数的第一个参数是err,用于保存在读取文件时返回的错误
6,回调函数的第二参数是打他,用户保存读取文件所返回的数据。
7,一旦文件被读取,回调就会被调用
8,如果err为真,那么就会抛出错误
9,如果err为假,那么来自文件的数据就可以使用
10,在本例中,数据会记录到控制台上。
 
再一个,http模块,http模块使得开发人员可以创建http客户端和服务器。

Js代码 

var http = require('http'); 
 
http.get({host:'shapeshed.com'},function(res){ 
  console.log("Got response:" + res.statusCode); 
}).on('error',function(e){ 
  console.log("Got error:" + e.message); 
 
});

 结果:Got response:200
1,请求http模块,以便在脚本中使用
2,给http.get()方法提供两个参数
3,第一个参数是选项对象。在本示例中,要求获取shapeshed.com的主页
4,第二个参数是一个以响应作为参数的回调函数
5,当远程服务器返回相应时,会触发回调函数。
6,在回调函数内记录响应状态码,如果有错误的话可以记录下来。
  
 接下来,我们看看有4个不同的I/O操作都在发生,他们都使用回调

Js代码 

var fs = require('fs'), 
  http = require('http'); 
 
http.get({host:'www.baidu.com'},function(res){ 
  console.log("baidu.com"); 
}).on('error',function(e){ 
  console.log("Got error:" + e.message); 
 
}); 
 
fs.readFile('somefile.txt','utf8',function(err,data){ 
  if(err) throw err; 
  console.log("somefile"); 
}); 
 
http.get({host:'www.duokan.com'},function(res){ 
  console.log("duokan.com"); 
}).on('error',function(e){ 
  console.log("Got error:" + e.message); 
 
}); 
 
fs.readFile('somefile2.txt','utf8',function(err,data){ 
  if(err) throw err; 
  console.log("somefile2"); 
});

 我们能知道哪个操作先返回吗?
猜测就是从磁盘上读取的两个文件先返回,因为无需进入网络,但是我们很难说哪个文件先返回,因为我们不知道文件的大小。对于两个主页的获取,脚本要进入网络,而响应时间则依赖于许多难以预测的事情,Node.js进程在还有已经注册的回调尚未触发之前将不会退出。回调首先解决不可预测性的方法,他也是处理并发(或者说一次做超过一件事情)的高效方法。
下面是我执行的结果

我的Node.js学习之路(三)--node.js作用、回调、同步和异步代码 以及事件循环
  
 同步和异步代码 

先看代码,同步(或者阻塞)代码

Js代码 

function sleep(milliseconds){ 
  var start = new Date().getTime(); 
  while((new Date().getTime() -start) < milliseconds){ 
 
  } 
} 
function fetchPage(){ 
  console.log('fetching page'); 
  sleep(2000); 
  console.log('data returned from requesting page'); 
} 
function fetchApi(){ 
  console.log('fetching api'); 
  sleep(2000); 
  console.log('data returned from the api'); 
} 
fetchPage(); 
fetchApi();

 
当脚本运行时,fetchPage()函数会被调用,直到它返回之前,脚本的运行是被阻塞的,在fetchPage()函数返回之前,程序是不能移到fetchApi()函数中的。这称为阻塞操作。
Node.js几乎从不使用这种编码风格,而是异步地调用回调。
看下下面编码,,

Js代码 

var http = require('http'); 
 
function fetchPage(){ 
  console.log('fetching page'); 
  http.get({host:'www.baidu.com',path:'/?delay=2000'}, 
    function(res){ 
      console.log('data returned from requesting page'); 
    }).on('error',function(e){ 
      console.log("There was an error" + e); 
    }); 
} 
function fetchApi(){ 
  console.log('fetching api'); 
  http.get({host:'www.baidu.com',path:'/?delay=2000'}, 
    function(res){ 
      console.log('data returned from requesting api'); 
    }).on('error',function(e){ 
      console.log("There was an error" + e); 
    }); 
} 
fetchPage(); 
fetchApi();

 允许这段代码的时候,就不再等待fetchPage()函数返回了,fetchApi()函数随之立刻被调用。代码通过使用回调,是非阻塞的了。一旦调用了,两个函数都会侦听远程服务器的返回,并以此触发回调函数。
注意这些函数的返回顺序是无法保证的,而是和网络有关。
 
事件循环

Node.js使用javascript的事件循环来支持它所推崇的异步编程风格。基本上,事件循环使得系统可以将回调函数先保存起来,而后当事件在将来发生时再运行。这可以是数据库返回数据,也可以是HTTP请求返回数据。因为回调函数的执行被推迟到事件反生之后,于是就无需停止执行,控制流可以返回到Node运行时的环境,从而让其他事情发生。

Node.js经常被当作是一个网络编程框架,因为它的设计旨在处理网络中数据流的不确定性。促成这样的设计的是事件循环和对回调的使用,他们似的程序员可以编写对网络或I/O事件进行响应的异步代码。

需要遵循的规则有:函数必须快速返回,函数不得阻塞,长时间运行的操作必须移到另一个进程中。
Node.js所不适合的地方包括处理大量数据或者长时间运行计算等。Node.js旨在网络中推送数据并瞬间完成。

Javascript 相关文章推荐
JS代码格式化和语法着色V2
Oct 14 Javascript
Javascript表达式中连续的 &amp;&amp; 和 || 之赋值区别
Oct 17 Javascript
为radio类型的INPUT添加客户端脚本(附加实现JS来禁用onClick事件思路代码)
Nov 11 Javascript
用Javascript来生成ftp脚本的小例子
Jul 03 Javascript
node.js实现逐行读取文件内容的代码
Jun 27 Javascript
详细分析JavaScript函数定义
Jul 16 Javascript
JavaScript类型系统之Object详解
Jan 07 Javascript
JS 动态加载js文件和css文件 同步/异步的两种简单方式
Sep 23 Javascript
详解vue静态资源打包中的坑与解决方案
Feb 05 Javascript
vue 属性拦截实现双向绑定的实例代码
Oct 24 Javascript
vue-music 使用better-scroll遇到轮播图不能自动轮播问题
Dec 03 Javascript
JS实现鼠标拖拽盒子移动及右键点击盒子消失效果示例
Jan 29 Javascript
我的Node.js学习之路(二)NPM模块管理
Jul 06 #Javascript
我的Node.js学习之路(一)
Jul 06 #Javascript
javascript数组操作方法小结和3个属性详细介绍
Jul 05 #Javascript
jQuery打印指定区域Html页面并自动分页
Jul 04 #Javascript
jQuery 仿百度输入标签插件附效果图
Jul 04 #Javascript
动态载入js提高网页打开速度的方法
Jul 04 #Javascript
JS中getYear()和getFullYear()区别分析
Jul 04 #Javascript
You might like
通过文字传递创建的图形按钮
2006/10/09 PHP
使用PHP数组实现无限分类,不使用数据库,不使用递归.
2006/12/09 PHP
yii2中LinkPager增加总页数和总记录数的实例
2017/08/28 PHP
php intval函数用法总结
2019/04/14 PHP
网站页面自动跳转实现方法PHP、JSP(下)
2010/08/01 Javascript
onkeydown事件解决按回车键直接提交数据的需求
2013/04/11 Javascript
jquery实现带单选按钮的表格行选中时高亮显示
2013/08/01 Javascript
js控制不同的时间段显示不同的css样式的实例代码
2013/11/04 Javascript
jquery 图片缩放拖动的简单实例
2014/01/08 Javascript
鼠标拖拽移动子窗体的JS实现
2014/02/25 Javascript
node.js Web应用框架Express入门指南
2014/05/28 Javascript
js查找节点的方法小结
2015/01/13 Javascript
jQuery无刷新切换主题皮肤实例讲解
2015/10/21 Javascript
深入浅析knockout源码分析之订阅
2016/07/12 Javascript
JS实现的tab切换选项卡效果示例
2017/02/28 Javascript
Bootstrap 响应式实用工具实例详解
2017/03/29 Javascript
JS判断微信扫码的方法
2017/08/07 Javascript
webpack vue项目开发环境局域网访问方法
2018/03/20 Javascript
vue自定义移动端touch事件之点击、滑动、长按事件
2018/07/10 Javascript
vue项目中实现图片预览的公用组件功能
2018/10/26 Javascript
JS中this的4种绑定规则详解
2020/02/04 Javascript
JS实现炫酷雪花飘落效果
2020/08/19 Javascript
[01:02:53]DOTA2上海特级锦标赛主赛事日 - 5 总决赛Liquid VS Secret第二局
2016/03/06 DOTA
[09:37]2018DOTA2国际邀请赛寻真——不懈追梦的Team Serenity
2018/08/13 DOTA
python创建和删除目录的方法
2015/04/29 Python
简单谈谈Python中的元祖(Tuple)和字典(Dict)
2017/04/21 Python
python判断计算机是否有网络连接的实例
2018/12/15 Python
set在python里的含义和用法
2019/06/24 Python
Python代码使用 Pyftpdlib实现FTP服务器功能
2019/07/22 Python
Python换行与不换行的输出实例
2020/02/19 Python
西班牙购买行李箱和背包网站:Maletas Greenwich
2019/10/08 全球购物
Pandora西班牙官方商店:PandoraShop.es
2020/10/05 全球购物
捷克建筑材料网上商店:DEK.cz
2021/03/06 全球购物
党章培训心得体会
2014/09/04 职场文书
2016年“11.11”光棍节活动总结
2016/04/05 职场文书
【海涛教你打DOTA】剑圣第一人称视角解说
2022/04/01 DOTA