timer 的 unref 函数
发布于 8 年前 作者 alsotang 14005 次浏览 来自 分享

我看遍了所有中文资料的 unref 资料,发现全是错的。。也是醉了。

官方文档:https://nodejs.org/dist/latest-v5.x/docs/api/timers.html#timers_unref

untitled1.png

来自:http://www.jb51.net/article/57861.htm

如果这个引用正确的话,那么《Node.js权威指南》这本书里的解释也是错的。

网上大部分的说法都说 unref 会暂停一个回调函数的调用,可实测的代码如下

var timer = setInterval(function () {
	console.log(new Date, 1)
}, 1000)


var timer2 = setInterval(function () {
	console.log(new Date, 2)
}, 1000)

timer2.unref()

timer2 始终没有被暂停。

按我对于文档的理解,unref 的意思只是让 timer 不会阻止 node 进程的退出,类似垃圾回收时候的引用计数-1的感觉。但不管是否调用了 unref,回调函数都会一直运行。


为了搜索引擎能过来,我堆点关键词:

javascript中setTimeout和setInterval的unref()和ref()

nodejs中的全局函数setTimeout/clearTimeout,setInterval/clearInterval,unref/ref

16 回复

取消不是 clearTimeout / clearInterval 么~哈哈

你上面的匿名函数是不行,但写成这种是可以的 var testFunction=function(){

 console.log("can cancel?");  

}

var timer=setInterval(testFunction,1000);

timer.unref();//取消setTimeout和setInterval的调用

timer.ref();//恢复setTimeout和setInterval的调用 unref取消的是它调用的函数

@jiangliqin

var timer = setInterval(function () {
	console.log(new Date, 1)
}, 1000)

var fn = function () {
	console.log(new Date, 2)
}

var timer2 = setInterval(fn, 1000)

timer2.unref()

你真的跑过?

@alsotang 根据你提供的官网文档, 这个地方but if it is the only item left in the event loop, it won't keep the program running指明如果调用了unref函数的定时器在事件循环中是仅存在的一个, 那么程序不会继续运行下去, 测试了下:

a> var timer = setInterval(function () {
	console.log(new Date, 1)
}, 1000)
var fn = function () {
	console.log(new Date, 2)
}
var timer2 = setInterval(fn, 1000)
timer2.unref()

函数中a处如果被注释掉, 此时相当于事件循环队列中只有一个timer2因此程序退出,如果把a处的setInterval换成setTimeout即让timer也仅运行一次, 那么timer和timer2都只会允许一次, 我理解的是timer 在的时候timer2在事件队列中不是唯一的因此它会继续执行.然而如果不调用unref函数不管怎么样程序都会一直运行下去

针对handle而言,判断loop是否存活只要看loop->active_handles是否大于0,大于0则存活。

具体代码参看 https://github.com/libuv/libuv/blob/v1.x/src/uv-common.h

uv__handle_init, uv__handle_start, uv__handle_stop, uv__handle_ref, uv__handle_unref

比较下面几种情况,可能会有利于理解unref的作用。

第一种

var timer1 = setTimeout(function(){
  console.log(new Date, 1);
}, 1000);
// setTimeout=>uv_timer_start(timer1)  active_handles = 1

var timer2 = setInterval(function(){
  console.log(new Date, 2);
}, 1000);
// setInterval=>uv_timer_start(timer2) active_handles = 2

// 1: ative_handles > 0 => loop()
// timer1 timeout => uv_timer_stop(timer1) active_handles = 1  => callback()
// timer2 timeout => uv_timer_stop(timer2) active_handles = 0  => callback() => uv_timer_start(timer2) active_handles = 1
// 2: active_handles > 0 =>  loop()
// timer2 timeout => uv_timer_stop(timer2) active_handles = 0  => callback() => uv_timer_start(timer2) active_handles = 1
// goto 2

第二种

var timer1 = setTimeout(function(){
  console.log(new Date, 1);
}, 1000);
// setTimeout=>uv_timer_start(timer1)  active_handles = 1

var timer2 = setInterval(function(){
  console.log(new Date, 2);
}, 1000);
// setInterval=>uv_timer_start(timer2) active_handles = 2

timer2.unref();
// uv_unref(timer2) active_handles = 1

// ative_handles > 0 => loop()
// timer1 timeout => uv_timer_stop(timer1) active_handles = 0  => callback()
// timer2 timeout => uv_timer_stop(timer2) active_handles = 0  => callback() => uv_timer_start(timer2) active_handles = 0
// active_handles == 0 =>  exit_process

第三种

var timer1 = setInterval(function(){
  console.log(new Date, 1);
}, 1000);
// setInterval=>uv_timer_start(timer1)  active_handles = 1

var timer2 = setInterval(function(){
  console.log(new Date, 2);
}, 1000);
// setInterval=>uv_timer_start(timer2) active_handles = 2

// 1: ative_handles > 0 => loop()
// timer1 timeout => uv_timer_stop(timer1) active_handles = 1  => callback() => uv_timer_start(timer1) active_handles = 2
// timer2 timeout => uv_timer_stop(timer2) active_handles = 1  => callback() => uv_timer_start(timer2) active_handles = 2
// goto 1

第四种

var timer1 = setInterval(function(){
  console.log(new Date, 1);
}, 1000);
// setInterval=>uv_timer_start(timer1)  active_handles = 1

var timer2 = setInterval(function(){
  console.log(new Date, 2);
}, 1000);
// setInterval=>uv_timer_start(timer2) active_handles = 2

timer2.unref()
// uv_unref(timer2) active_handles = 1

// 1: ative_handles > 0 => loop()
// timer1 timeout => uv_timer_stop(timer1) active_handles = 0  => callback() => uv_timer_start(timer1) active_handles = 1
// timer2 timeout => uv_timer_stop(timer2) active_handles = 1  => callback() => uv_timer_start(timer2) active_handles = 1
// goto 1

第五种

var timer1 = setInterval(function(){
  console.log(new Date, 1);
}, 1000);
// setInterval=>uv_timer_start(timer1)  active_handles = 1

timer1.unref()
// uv_unref(timer1) active_handles = 0

var timer2 = setInterval(function(){
  console.log(new Date, 2);
}, 1000);
// setInterval=>uv_timer_start(timer2) active_handles = 1

timer2.unref()
// uv_unref(timer2) active_handles = 0

// ative_handles == 0 => exit process

@jiangliqin

@alsotang@ncuzp 的理解是正确的,主要还是看循环是否结束,循环结束了,进程退出了,回调函数当然不会执行了。

uv__hanlde_init(handle) => handle->flags = UV__HANDLE_REF

uv__handle_unref(handle) 清除 handle->flags上的UV__HANDLE_REF位,没有被ref标记位,uv__handle_start(handle), uv__handle_stop(handle) 就不会影响loop->active_handles。

一些文章要么是错的,要么是过时的,最好还是直接去看源代码。https://github.com/libuv/libuv/blob/v1.x/src/uv-common.h

@coordcn 向大神学习,感叹知道的好少

@coordcn 学习了,看了express-rate-limit这个中间件的实现 ,里面有用到unref,当时也不理解。 ‘use strict’; function MemoryStore(windowMs) { var hits = {};

  this.incr = function(key, cb) {
	  if (hits[key]) {
		  hits[key]++;
	  } else {
		  hits[key] = 1;
	  }

	  cb(null, hits[key]);
  };

  this.resetAll = function() {
	  hits = {};
  };

  // export an API to allow hits from one or all IPs to be reset
  this.resetKey = function(key) {
	  delete hits[key];
  };

  // simply reset ALL hits every windowMs
  var interval = setInterval(this.resetAll, windowMs);
  if (interval.unref) {
	interval.unref();
  }
}

module.exports = MemoryStore;

专门注册一个账号膜拜大佬~~

回到顶部