资讯专栏INFORMATION COLUMN

node异步非阻塞的杂谈

izhuhaodev / 783人阅读

摘要:引言作为服务器的优势就在于适合处理高并发的请求,对于网站后台这种密集型的后台尤其有优势,其核心就在于是一个异步非阻塞模型。关于异步,同步,阻塞,非阻塞这些概念,本文不做讨论。另外两个的调用时间需要判断是否都在主线程中被执行。

引言

node作为服务器的优势就在于适合处理高并发的请求,对于web网站后台这种I/O密集型的后台尤其有优势,其核心就在于node是一个异步非阻塞模型。关于异步,同步,阻塞,非阻塞这些概念,本文不做讨论。

node的单线程模型

js作为单线程语言,有自己的一套运行机制

同步任务运行在主线程上,异步的所有任务都会在另一个队列中等待执行,一旦同步任务执行完毕开始执行异步队列中的任务,此时可以认为将第一个异步队列中的任务移到主线程,一旦再产生异步操作,就会继续往异步队列中添加,以此循环。这就是为什么promise,setTimeout,setInterval,process.nextTick,setImmediate,ajax请求,看起来虽然位于代码的上面部分却没有被按顺序执行。
看一下node的eventloop的机制,也就是node实现异步的架构。

主要的不同点是用LIBUV去将队列中的任务形成一个eventloop,作为下一个循环需要执行的工作。

观察者模式的体现

个人一直认为js的异步回调是一种观察者模式的体现,订阅/发布,网上的说法是有三种观察者

idle观察者:顾名思义,就是早已等在那里的观察者,以后会说到的process.nextTick就属于这类

I/O观察者:顾名思义,就是I/O相关观察者,也就是I/O的回调事件,如网络,文件,数据库I/O等

check观察者:顾名思义,就是需要检查的观察者,后面会说到的setTimeout/setInterval就属于这类

优先级idle观察者>I/O观察者>check观察者

详细链接

但是个人看法setTimeout()和setInterval()可以归为一类观察者,算是timer观察者,setImmediate()是check观察者,至于原因后面回说明

以下是自己对setTimeout(),setImmediate()和process.nextTick()的比较

首先所有讨论均是建立在node的基础上,三个函数也都只比较分析node 中情况,摘自node文档

setTimeout()
callback  The function to call when the timer elapses.
delay The number of milliseconds to wait before calling the callback.

Schedules execution of a one-time callback after delay milliseconds. Returns a Timeout for use with clearTimeout().

The callback will likely not be invoked in precisely delay milliseconds. Node.js makes no guarantees about the exact timing of when callbacks will fire, nor of their ordering. The callback will be called as close as possible to the time specified.

这里明确了两点,一个是settimeout的实际执行时间必然晚于设置时间,作精确定时器根本就是违背他的设计意愿的,另外一点就是他的函数回调是timer观察的。

setImmediate()
callback  The function to call at the end of this turn of the Node.js Event Loop
...args Optional arguments to pass when the callback is called.

Schedules the "immediate" execution of the callback after I/O events" callbacks. Returns an Immediate for use with clearImmediate().
When multiple calls to setImmediate() are made, the callback functions are queued for execution in the order in which they are created. The entire callback queue is processed every event loop iteration. If an immediate timer is queued from inside an executing callback, that timer will not be triggered until the next event loop iteration.

核心,回调会被立刻放在eventLoop的末尾

process.nextTick()
callback 
...args Additional arguments to pass when invoking the callback

he process.nextTick() method adds the callback to the "next tick queue". Once the current turn of the event loop turn runs to completion, all callbacks currently in the next tick queue will be called.
This is not a simple alias to setTimeout(fn, 0). It is much more efficient. It runs before any additional I/O events (including timers) fire in subsequent ticks of the event loop.

文档中自己就提到了process.nextTick()并非 setTimeout(fn, 0),他更有效率,并且执行的序列必在下次所有的event loop的最前列。

比较

之后在node文档中有一个比较细致的比较,链接

比较这三个函数,先说process.nextTick(),文档中说了process.nextTick() is not technically part of the event loop,现在很明确了process.nextTick()并不在event loop里,他回调的执行是在事件等待队列之外的,算是优先级最高的插队人员,那它作为最优先执行回调的就没有疑问了,实际的用处就是有一些必须最优先执行的回调,比如网络服务端中,端口的监听应该必须早于其他事件的回调。

setImmediate() vs setTimeout()

这个可能是网上说法最不统一的地方了,说谁先执行的都有,先测了下面的代码

setTimeout(() => {
 console.log("timeout");
}, 0);

setImmediate(() => {
 console.log("immediate");
});

结果还真的是随机的,这么不严谨?其实官方对这个是有解释的

The order in which the timers are executed will vary depending on the context in which they are called. If both are called from within the main module, then timing will be bound by the performance of the process (which can be impacted by other applications running on the machine).
However, if you move the two calls within an I/O cycle, the immediate callback is always executed first:

这个教育了我,真理是有范围的,所谓普遍真理是形而上学。所以说setImmediate() vs setTimeout()谁快不能简单的说,必须先讨论使用的地方。

上面有提到setTimeout(fn, 0)效率不高,至于为什么,暂时参照国内普遍的说法 该函数的事件控制,是被维护在红黑树上,那么为了每次去找超时的回调必然是logn的复杂度,而另外两个函数看起来都应该是1的复杂度

总结

综上个人倾向于四种观察者的说法

至于setImmediate() vs setTimeout() vs process.nextTick(),process.nextTick()最快,也有独特的应用场景。另外两个的调用时间需要判断是否都在主线程中被执行。

setTimeout(fn, 0)效率偏低。

tip:node官方建议使用setimmediate(),因为至少应用的范围就可以到浏览器端了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107639.html

相关文章

  • Node.js 指南(阻塞阻塞概述)

    摘要:标准库中的所有方法都提供非阻塞的异步版本,并接受回调函数,某些方法还具有对应的阻塞方法,其名称以结尾。比较代码阻塞方法同步执行,非阻塞方法异步执行。 阻塞与非阻塞概述 此概述介绍了Node.js中阻塞与非阻塞调用之间的区别,此概述将引用事件循环和libuv,但不需要事先了解这些主题,假设读者对JavaScript语言和Node.js回调模式有基本的了解。 I/O主要指与libuv支持的...

    zebrayoung 评论0 收藏0
  • node - 阻塞异步 IO

    摘要:而线程是进程的一部分,二者相扶相依,其中单线程被称为轻权进程或轻量级进程,执行特性线程只有个基本状态就绪,执行,阻塞。以上所述证明了操作与其他函数的这种区别是由实现,是用多线程的方式,在标准的阻塞式上模拟非阻塞异步,线程池默认限制四线程。 node - 非阻塞的异步 IO 每当我们提起 node.js 时总会脱口而出 事件驱动、非阻塞I/O 和 单线程,所以我总结了以下几点对这三项概念...

    yuxue 评论0 收藏0
  • node - 阻塞异步 IO

    摘要:而线程是进程的一部分,二者相扶相依,其中单线程被称为轻权进程或轻量级进程,执行特性线程只有个基本状态就绪,执行,阻塞。以上所述证明了操作与其他函数的这种区别是由实现,是用多线程的方式,在标准的阻塞式上模拟非阻塞异步,线程池默认限制四线程。 node - 非阻塞的异步 IO 每当我们提起 node.js 时总会脱口而出 事件驱动、非阻塞I/O 和 单线程,所以我总结了以下几点对这三项概念...

    keelii 评论0 收藏0
  • node - 阻塞异步 IO

    摘要:而线程是进程的一部分,二者相扶相依,其中单线程被称为轻权进程或轻量级进程,执行特性线程只有个基本状态就绪,执行,阻塞。以上所述证明了操作与其他函数的这种区别是由实现,是用多线程的方式,在标准的阻塞式上模拟非阻塞异步,线程池默认限制四线程。 node - 非阻塞的异步 IO 每当我们提起 node.js 时总会脱口而出 事件驱动、非阻塞I/O 和 单线程,所以我总结了以下几点对这三项概念...

    hankkin 评论0 收藏0
  • node - 阻塞异步 IO

    摘要:而线程是进程的一部分,二者相扶相依,其中单线程被称为轻权进程或轻量级进程,执行特性线程只有个基本状态就绪,执行,阻塞。以上所述证明了操作与其他函数的这种区别是由实现,是用多线程的方式,在标准的阻塞式上模拟非阻塞异步,线程池默认限制四线程。 node - 非阻塞的异步 IO 每当我们提起 node.js 时总会脱口而出 事件驱动、非阻塞I/O 和 单线程,所以我总结了以下几点对这三项概念...

    oneasp 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<