Node.js是单线程的,基于事件循环,非阻塞 IO的。事件循环中使用一个事件队列,在每个时间点上,系统只会处理一个事件,即使电脑有多个CPU核心,也无法同时并行的处理多个事件。因此,node.js适合处理I/O型的应用,不适合那种CPU运算密集型的应用。在I/O型的应用中,给每一个输入输出定义一个回调函数,node.js会自动将其加入到事件轮询的处理队列里,当I/O操作完成后,这个回调函数会被触发,系统会继续处理其他的请求。

在这里用debuggable.com上的那个文章中的一段比喻来讲,非常容易理解。如下:

我们写的js代码就像是一个国王,而nodejs给国王提供了很多仆人。早上,一个仆人叫醒了国王,问他有什么需要。国王给他一份清单,上面列举了所有需要完成的任务,然后睡回笼觉去了。当国王回去睡觉之后,仆人才离开国王,拿着清单,给其它的仆人一个个布置任务。仆人们各自忙各自的去了,直到完成了自己的任务后,才回来把结果禀告给国王。国王一次只召见一个人,其它的人就在外面排着队等着。国王处理完这个结果后,可能给他布置一个新的任务,或者就直接让他走了,然后再召见下一个人。等所有的结果都处理完了,国王就继续睡觉去了。直接有新的仆人完成任务后过来找他。这就是国王的幸福生活。

process.nextTick(callback)

功能:在事件循环的下一次循环中调用 callback 回调函数。效果是将一个函数推迟到代码书写的下一个同步方法执行完毕时异步方法的事件回调函数开始执行时;与setTimeout(fn, 0) 函数的功能类似,但它的效率高多了。

基于node.js的事件循环分析,每一次循环就是一次tick,每一次tick时,v8引擎从事件队列中取出所有事件依次进行处理,如果遇到nextTick事件,则将其加入到事件队尾,等待下一次tick到来时执行;造成的结果是,nextTick事件被延迟执行;以下是nextTick源码

从这几行代码中,我们可以看出很多信息:

  1. nextTick的确是把某任务放在队列的最后(array.push)
  2. nodejs在执行任务时,会一次性把队列中所有任务都拿出来,依次执行
  3. 如果全部顺利完成,则删除刚才取出的所有任务,等待下一次执行
  4. 如果中途出错,则删除已经完成的任务和出错的任务,等待下次执行
  5. 如果第一个就出错,则throw error

下面看一下应用场景(包含计算密集型操作,将其进行递归处理,而不阻塞进程):

  1. var http = require('http');
  2. var wait = function (mils) {
  3.     var now = new Date;
  4.     while (new Date - now <= mils);
  5. };
  6. function compute() {
  7.     // performs complicated calculations continuously
  8.     console.log('start computing');
  9.     wait(1000);
  10.     console.log('working for 1s, nexttick');
  11.     process.nextTick(compute);
  12. }
  13. http.createServer(function (req, res) {
  14.     console.log('new request');
  15.     res.writeHead(200, {'Content-Type': 'text/plain'});
  16.     res.end('Hello World');
  17. }).listen(5000, '127.0.0.1');
  18. compute();

1、其中compute是一个密集计算的函数,我们把它变为可递归的,每一步需要1秒(使用wait来代替密集运行)。执行完一次后,通过process.nextTick把下一次的执行放在队列的尾部,转而去处理已经处于等待中的客户端请求。这样就可以同时兼顾两种任务,让它们都有机会执行。

关于node.js中处理计算密集型,可以参考以下几种方法:

  • c/c++的addon来实现,在需要进行cpu密集型计算的地方,把js代码改写成c/c++代码;对于不熟悉C++代码,成本较高
  • 使用cluster创建多进程处理,但编码复杂度比较高;
  • 让node支持多线程模型的模块:threads_a_gogogithub地址:https://github.com/xk/node-threads-a-gogo(比较好用一些,参考介绍

2、另外:异步模型的关系,导致某些代码的执行可能先于它们所需要的条件完成之前,所以将这些需要先置条件的代码放入到一个回调函数中,然后放入到下一个事件循环的顶层。那么这些代码就不会被立刻执行了,而是在下一轮事件启动之前等待,启动后在进行执行。

范例:

var MyConstructor = function() {
...
process.nextTick(function() {
self._continue();
});
};
 
MyConstructor.prototype.__proto__ = EventEmitter.prototype;
 
MyConstructor.prototype._continue = function() {
// without the process.nextTick
// these events would be emitted immediately
// with no listeners. they would be lost.
this.emit('data', 'hello');
this.emit('data', 'world');
this.emit('end');
};
 
function(req, res, next) {
var c = new MyConstructor(...);
c.on('data', function(data) {
console.log(data);
});
c.on('end', next);
}

Node.js的process.nextTick(callback)理解的更多相关文章

  1. Node.js中Process.nextTick()和setImmediate()的区别

    一.Webstrom使用node.js IDE的问题 在区别这两个函数之前来说一下Webstrom使用node.js IDE的问题,在配置Node.js的IDE了,但setImmediate().re ...

  2. Node.js的process模块

    process模块用来与当前进程互动,可以通过全局变量process访问,不必使用require命令加载.它是一个EventEmitter对象的实例. 属性 process对象提供一系列属性,用于返回 ...

  3. node.js中process进程的概念和child_process子进程模块的使用

    进程,你可以把它理解成一个正在运行的程序.node.js中每个应用程序都是进程类的实例对象. node.js中有一个 process 全局对象,通过它我们可以获取,运行该程序的用户,环境变量等信息. ...

  4. 《深入浅出Node.js》第6章 理解 Buffer

    @by Ruth92(转载请注明出处) 第6章 理解 Buffer ✁ 为什么需要 Buffer? 在 Node 中,应用需要处理网络协议.操作数据库.处理图片.接收上传文件等,在网络流和文件的操作中 ...

  5. node中定时器, process.nextTick(), setImediate()的区别与联系

    1.定时器 setTimeout()和setInterval()与浏览器中的API是一致的,定时器的问题在于,他并非精确的(在容忍范围内).尽管事件循环十分快,但是如果某一次循环占用的时间较多,那么下 ...

  6. [Node.js] 04 - Event and Callback

    回调函数 回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数. 异步读取文件的回调函数: var fs = require("fs&quo ...

  7. node.js中module模块的理解

    node.js中使用CommonJS规范实现模块功能,一个单独的文件就是一个单独的模块.通过require方法实现模块间的依赖管理. 通过require加载模块,是同步操作. 加载流程如下: 1.找到 ...

  8. node.js 中回调函数callback(转载),说的很清楚,看一遍就理解了

    最近在看 express,满眼看去,到处是以函数作为参数的回调函数的使用.如果这个概念理解不了,nodejs.express 的代码就会看得一塌糊涂.比如: 复制代码 代码如下: app.use(fu ...

  9. Node.js 中 process.cwd()与__dirname的区别

    process.cwd() 是当前执行node命令时候的文件夹地址 --工作目录,保证了文件在不同的目录下执行时,路径始终不变 __dirname 是被执行的js 文件的地址 --文件所在目录 当前模 ...

随机推荐

  1. vb小程序浅析

    系统 : Windows xp 程序 : BJCM10B 程序下载地址 :http://pan.baidu.com/s/1dFyXe29 要求 : 编写注册机 使用工具 : OD 可在看雪论坛中查找关 ...

  2. Voreen (二) 入点出点计算

    继第一篇Voreen的文章介绍主流程以后,第二篇介绍Raycast的第一个绘制Pass,根据代理几何体绘制出入点出点.如上次所说,OptimizedProxyGeometry负责生成表示体数据的代理几 ...

  3. Android 学习第13课,android 实现发送短信的功能

    1. 界面布局 界面代码: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ...

  4. Android DownloadProvider学习

    DownloadProvider 简介 DownloadProvider 是Android提供的DownloadManager的增强版,亮点是支持断点下载,提供了“开始下载”,“暂停下载”,“重新下载 ...

  5. Linux标准出错重定向导出

    ~$ ls han >1.txt 2>&1

  6. Qt4编码

    #if QT_VERSION < 0x050000 qDebug() << "qt5以下的版本, 从QTextCodec设置全局字符集"; QTextCodec* ...

  7. PHP 下option selected 无效

    针对于PHP 下的selected="selected" 赋值无效 <select autocomplete="off" ></select& ...

  8. web安全之文件上传漏洞

    成因: 当文件上传时,若服务端脚本语言未对上传的文件进行严格验证和过滤,若恶意用户上传恶意的 脚本文件时,就有可能控制整个网站甚至是服务器,这就是文件上传漏洞. 权限: 1. 后台权限:登陆了后台,可 ...

  9. JDBC使用事务实例

    package qddx.JDBC; import java.sql.*; public class useTransaction { public static void main(String[] ...

  10. Binary Tree Preorder Traversal

    Given a binary tree, return the preorder traversal of its nodes' values. For example:Given binary tr ...