链接:http://www.runoob.com/nodejs/nodejs-callback.html

首先什么是单线程异步非阻塞?

单线程的意思整个程序从头到尾但是运用一个线程,程序是从上往下执行的。异步操作就是程序虽然是从上到下执行的,但是某个函数执行时间过长时并不会阻塞在那里等待它执行完,然后在执行下面的代码。非阻塞也就是这个意思。

为什么node是异步非阻塞的呢,得力于回调函数,还有js中的定时器也是经典的异步操作。

###4.1 Node.js异步机制 由于异步的高效性,node.js设计之初就考虑做为一个高效的web服务器,作者理所当然地使用了异步机制,并贯穿于整个node.js的编程模型中,新手在使用node.js编程时,往往会羁绊于由于其他编程语言的习惯,比如C/C++,觉得无所适从。我们可以从以下一段简单的睡眠程序代码窥视出他们的区别,下面是摘自《linux程序设计》打印10个时间的C代码:

#include <time.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
int i;
time_t the_time;
for(i = ; i <= ; i++) {
the_time = time((time_t *));
printf("The time is %ld\n", the_time);
sleep();
}
exit();
}

编译后打印结果如下:

The time is 1396492137

The time is 1396492139

The time is 1396492141

The time is 1396492143

The time is 1396492145

The time is 1396492147

The time is 1396492149

The time is 1396492151

The time is 1396492153

The time is 1396492155

从C语言的打印结果可以发现,是隔2秒打印一次,按照C程序该有的逻辑,代码逐行执行。以下Node.js代码本意如同上述C代码,使用目的隔2秒打印一次时间,共打印10条(初次从C/C++转来接触Node.js的程序员可能会写出下面的代码):

function test() {
for (var i = 0; i < 10; i++) {
console.log(new Date);
setTimeout(function(){}, 2000); //睡眠2秒,然后再进行一下次for循环打印
}
};
test();

打印结果: Tue Apr 01 2014 14:53:22 GMT+0800 (中国标准时间)
Tue Apr 01 2014 14:53:22 GMT+0800 (中国标准时间)
Tue Apr 01 2014 14:53:22 GMT+0800 (中国标准时间)
Tue Apr 01 2014 14:53:22 GMT+0800 (中国标准时间)
Tue Apr 01 2014 14:53:22 GMT+0800 (中国标准时间)
Tue Apr 01 2014 14:53:22 GMT+0800 (中国标准时间)
Tue Apr 01 2014 14:53:22 GMT+0800 (中国标准时间)
Tue Apr 01 2014 14:53:22 GMT+0800 (中国标准时间)
Tue Apr 01 2014 14:53:22 GMT+0800 (中国标准时间)
Tue Apr 01 2014 14:53:22 GMT+0800 (中国标准时间)
观察结果发现都是在14:53:22同一个时间点打印的,根本就没有睡眠2秒后再执行下一轮循环打印!这是为什么?从官方的文档我们看出setTimeout是第二个参数表示逝去时间之后在执行第一个参数表示的callback函数,因此我们可以分析, 由于Node.js的异步机制,setTimeout每个for循环到此之后,都注册了一个2秒后执行的回调函数然后立即返回马上执行console.log(new Date),导致了所有打印的时间都是同一个点,因此我们修改for循环的代码如下:

for (var i = 0; i < 10; i++) {
setTimeout(function(){
console.log(new Date);
}, 2000); }

执行结果如下所示: Thu Apr 03 2014 09:30:35 GMT+0800 (中国标准时间)
Thu Apr 03 2014 09:30:35 GMT+0800 (中国标准时间)
Thu Apr 03 2014 09:30:35 GMT+0800 (中国标准时间)
Thu Apr 03 2014 09:30:35 GMT+0800 (中国标准时间)
Thu Apr 03 2014 09:30:35 GMT+0800 (中国标准时间)
Thu Apr 03 2014 09:30:35 GMT+0800 (中国标准时间)
Thu Apr 03 2014 09:30:35 GMT+0800 (中国标准时间)
Thu Apr 03 2014 09:30:35 GMT+0800 (中国标准时间)
Thu Apr 03 2014 09:30:35 GMT+0800 (中国标准时间)
Thu Apr 03 2014 09:30:35 GMT+0800 (中国标准时间)
神奇,仍然是同一个时间点,见鬼!冷静下来分析,时刻考虑异步,for循环里每次setTimeout注册了2秒之后执行的一个打印时间的回调函数,然后立即返回,再执行setTimeout,如此反复直到for循环结束,因为执行速度太快,导致同一个时间点注册了10个2秒后执行的回调函数,因此导致了2秒后所有回调函数的立即执行。 我们在for循环之前添加console.log("before FOR: " + new Date)和之后console.log("after FOR: " + new Date),来验证我们的推测,打印结果如下(后面省略8条相同的打印行):
before FOR: Thu Apr 03 2014 09:42:43 GMT+0800 (中国标准时间)
after FOR: Thu Apr 03 2014 09:42:43 GMT+0800 (中国标准时间)
Thu Apr 03 2014 09:42:45 GMT+0800 (中国标准时间)
Thu Apr 03 2014 09:42:45 GMT+0800 (中国标准时间)
…… (省略与上一行8条相同的打印行)
由此可以窥视出Node.js异步机制的端倪了,在for循环中的代码于其后的代码几乎在一个单位秒内完成,而定时器中的回调函数则按要求的2秒之后执行,也是同一秒内执行完毕。那么如何实现最初C语言每隔2秒打印一个系统时间的需求函数呢,我实现了如下一个wsleep函数,放在for循环中,可以达到该目的:

function wsleep(milliSecond) {
var startTime = new Date().getTime();
while(new Date().getTime() <= milliSecond + startTime) {
}
}

但是该函数有一个令他无法在项目中使用的缺陷,请问为什么?

如果没有回调函数的话,就变成阻塞式的,程序基本上是从上到下执行的。主要就是因为回调函数的影响导致的

2.阻塞与非阻塞

Node.js 回调函数

Node.js 异步编程的直接体现就是回调。

异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。

回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数。

例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 Node.js 的性能,可以处理大量的并发请求。


阻塞代码实例

创建一个文件 input.txt ,内容如下:

菜鸟教程官网地址:www.runoob.com

创建 main.js 文件, 代码如下:

var fs = require("fs");

var data = fs.readFileSync('input.txt');

console.log(data.toString());
console.log("程序执行结束!");

以上代码执行结果如下:

$ node main.js
菜鸟教程官网地址:www.runoob.com 程序执行结束!

非阻塞代码实例

创建一个文件 input.txt ,内容如下:

菜鸟教程官网地址:www.runoob.com

创建 main.js 文件, 代码如下:

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
}); console.log("程序执行结束!");

以上代码执行结果如下:

$ node main.js
程序执行结束!
菜鸟教程官网地址:www.runoob.com

以上两个实例我们了解了阻塞与非阻塞调用的不同。第一个实例在文件读取完后才执行完程序。 第二个实例我们呢不需要等待文件读取完,这样就可以在读取文件时同时执行接下来的代码,大大提高了程序的性能。

因此,阻塞按是按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,我们就需要写在回调函数内。

node 单线程异步非阻塞的更多相关文章

  1. 理解Node.js异步非阻塞I/O与传统线性阻塞IO的区别(转)

    阻塞I/O 程序执行过程中必然要进行很多I/O操作,读写文件.输入输出.请求响应等等.I/O操作时最费时的,至少相对于代码来说,在传统的编程模式中,举个例子,你要读一个文件,整个线程都暂停下来,等待文 ...

  2. 爬虫基础--IO多路复用单线程异步非阻塞

    最近一直的学习爬虫  ,进行基础的学习 性能相关 参考 https://www.cnblogs.com/wupeiqi/p/6229292.html # 目标:单线程实现并发HTTP请求 # # so ...

  3. Tornado的异步非阻塞

    阻塞和非阻塞Web框架 只有Tornado和Node.js是异步非阻塞的,其他所有的web框架都是阻塞式的. Tornado阻塞和非阻塞两种模式都支持. 阻塞式: 代表:Django.Flask.To ...

  4. nodejs的异步非阻塞IO

    简单表述一下:发启向系统IO操作请求,系统使用线程池IO操作,执行完放到事件队列里,node主线程轮询事件队列,读取结果与调用回调.所以说node并非真的单线程,还是使用了线程池的多线程. 上个图看看 ...

  5. 在nginx启动后,如果我们要操作nginx,要怎么做呢 别增加无谓的上下文切换 异步非阻塞的方式来处理请求 worker的个数为cpu的核数 红黑树

    nginx平台初探(100%) — Nginx开发从入门到精通 http://ten 众所周知,nginx性能高,而nginx的高性能与其架构是分不开的.那么nginx究竟是怎么样的呢?这一节我们先来 ...

  6. Python web框架 Tornado(二)异步非阻塞

    异步非阻塞 阻塞式:(适用于所有框架,Django,Flask,Tornado,Bottle) 一个请求到来未处理完成,后续一直等待 解决方案:多线程,多进程 异步非阻塞(存在IO请求): Torna ...

  7. Python的异步编程[0] -> 协程[1] -> 使用协程建立自己的异步非阻塞模型

    使用协程建立自己的异步非阻塞模型 接下来例子中,将使用纯粹的Python编码搭建一个异步模型,相当于自己构建的一个asyncio模块,这也许能对asyncio模块底层实现的理解有更大的帮助.主要参考为 ...

  8. Python异步非阻塞IO多路复用Select/Poll/Epoll使用,线程,进程,协程

    1.使用select模拟socketserver伪并发处理客户端请求,代码如下: import socket import select sk = socket.socket() sk.bind((' ...

  9. nginx学习(二)——基础概念之异步非阻塞

    上面讲了很多关于nginx的进程模型,接下来,我们来看看nginx是如何处理事件的. 有人可能要问了,nginx采用多worker的方式来处理请求,每个worker里面只有一个主线程,那能够处理的并发 ...

随机推荐

  1. 【LGR-060】洛谷10月月赛 I

    A - 打字练习 出题:memset0 送分模拟题,按题意模拟即可. 需要注意的是对退格键的判断,如果光标已经在行首,则直接忽略被读入的退格键. B - 小猪佩奇爬树 出题:_QAQ 维护所有相同节点 ...

  2. 7.18 NOIP模拟测试5 星际旅行+砍树+超级树

    T1 星际旅行 题意:n个点,m条边,无重边,有自环,要求经过m-2条边两次,2条边一次,问共有多少种本质不同的方案.本质不同:当且仅当至少存在一条边经过次数不同. 题解:考试的时候理解错题,以为他是 ...

  3. [LeetCode] 490. The Maze 迷宫

    There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolli ...

  4. [LeetCode] 147. Insertion Sort List 链表插入排序

    Sort a linked list using insertion sort. A graphical example of insertion sort. The partial sorted l ...

  5. linux 修改oracle的字符集

    select   userenv('language')   from   dual; 命令可以查看服务端的使用的字符集. ssh登录,切换到oracle用户 切换用户命令:su -oracle 之后 ...

  6. Navicat Premium 12 安装与激活

    一.Navicat Premium 12下载 官方下载地址:https://www.navicat.com.cn/download/navicat-premium 百度云盘:https://pan.b ...

  7. 用siege测试接口高并发

    siege -c 255 -r 2555 "http://10.1.1.6:3001/decode POST <./api.json" -t 100s

  8. Redis(九)高可用专栏之《简介篇》

    在互联网的大趋势下,用户体验.服务的可用性日趋重要.任何一个服务的不可用,都可能导致连锁式功能故障. 前言 高可用模型的已经逐渐形成一种套路: 主备/主从模式 集群模式 主备/主从模式 至少有两台服务 ...

  9. Go语言-1-标识符与变量

    目录 1. Go标识符 1.1 Go关键字 1.2 常量标识符(4个) 1.3 空白标识符(1个) 1.4 内置数据类型标识符 1.5 内置函数(15个) 2. Go语言操作符 3. Go语言变量 3 ...

  10. “sgen.exe”未能运行。文件名或扩展名太长

    问题 创建项目后无法运行 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 MSB6003 指定的任务可执行文件"sgen.exe"未能运行.System.Component ...