Node timer 使用技巧和源码分析

栏目: 数据库 · 发布时间: 6年前

内容简介:Node.js 中的定时器函数与web浏览器中的定时函数API 类似,增加了一个setImmediate() 函数,它们向任务队列添加定时任务setTimeout(callback, delay) delay毫秒后执行回掉函数setInterval(callback,delay) 每隔delay毫秒执行一次回掉函数

Node.js 中的定时器函数与web浏览器中的定时函数API 类似,增加了一个setImmediate() 函数,它们向任务队列添加定时任务

介绍

setTimeout(callback, delay) delay毫秒后执行回掉函数

setInterval(callback,delay) 每隔delay毫秒执行一次回掉函数

setImmediate() 将在当前事件轮询的末尾处执行。同步任务执行完后,delay不为0时首先立即执行setImmediate() 函数

console.log("1")

setTimeout(func, 1000,10);
function func(num) {
    console.log("2")
  }
  
setImmediate(function(){console.log("3")}, 2000,10);

console.log("4")
//输出 1 4 3 2
复制代码

取消定时器,参数为每个定时器函数返回的定时器对象

clearTimeout(timeout)

clearInterval(timeout)

clearImmediate(immediate)

使用

//tcp服务端
var net = require('net')
var sever = net.createServer(function (connection) {
  //设置服务超时时间,并返回提示消息
  connection.setTimeout(1000, function () {
    console.log("响应超时.");
    connection.write('服务端响应超时了')
  });
  setTimeout(function () {
    connection.on('end', function () {
      //   console.log('客户端关闭连接')
    })
    connection.on('data', function (data) {
      console.log('服务端:收到客户端发送数据为' + data.toString())
    })
    //给客户端响应的数据
    connection.write('response hello')
  }, 5000)

})
sever.listen(8080, function () {
  console.log('监听端口:8080')
})
复制代码

HTTP服务器端开始发送响应数据到HTTP客户端接收全部数据的这段时间, 如果超出设定时间,则表示响应超时,并返回超时提示

var net = require('net')
var client = net.connect({
    port: 8080
})

//设置请求超时时间
client.setTimeout(3000);
//监听超时事件
client.on('timeout', function () {
    console.log("请求超时")
    //取消请求数据,不再发送请求
    client.destroy()
})
//客户端收到服务端执行的事件
client.on('data', function (data) {
    console.log('客户端:收到服务端响应数据为' + data.toString())
    client.end()
})
//给服务端传递的数据

client.write('hello')
client.on('end', function () {
    // console.log('断开与服务器的连接')
})
复制代码

客户端设置请求超时时间,HTTP客户端发起请求到接受到HTTP服务器端返回响应头的这段时间, 如果超出设定时间,就终止请求

源码分析

lib/timers.js

setTimeout函数定义

function setTimeout(callback, after, arg1, arg2, arg3) {
//第一个参数必须为函数
  if (typeof callback !== 'function') {
    throw new ERR_INVALID_CALLBACK(callback);
  }
//处理参数
//将第三个以后的参数包装成数组
  var i, args;
  switch (arguments.length) {
    // fast cases
    case 1:
    case 2:
      break;
    case 3:
      args = [arg1];
      break;
    case 4:
      args = [arg1, arg2];
      break;
    default:
      args = [arg1, arg2, arg3];
      for (i = 5; i < arguments.length; i++) {
        // Extend array dynamically, makes .apply run much faster in v6.0.0
        args[i - 2] = arguments[i];
      }
      break;
  }
    //生成一个Timeout 对象
  const timeout = new Timeout(callback, after, args, false);
  active(timeout);
//返回一个定时器对象
  return timeout;
}
复制代码

Timeout构造函数

const timeout = new Timeout(callback, after, args, false);

lib/internal/timers.js

生成的timer实例 表示Node.js层面的定时器对象,比如 setTimeout、setInterval、setImmediate返回的对象

function Timeout(callback, after, args, isRepeat) {
  after *= 1; // Coalesce to number or NaN
  if (!(after >= 1 && after <= TIMEOUT_MAX)) {
    if (after > TIMEOUT_MAX) {
      process.emitWarning(`${after} does not fit into` +
                          ' a 32-bit signed integer.' +
                          '\nTimeout duration was set to 1.',
                          'TimeoutOverflowWarning');
    }
    after = 1; // Schedule on next tick, follows browser behavior
  }
  //延迟时间
  this._idleTimeout = after;
  //前后指针
  this._idlePrev = this;
  this._idleNext = this;
  this._idleStart = null;
  // This must be set to null first to avoid function tracking
  // on the hidden class, revisit in V8 versions after 6.2
  this._onTimeout = null;
  //回调函数
  this._onTimeout = callback;
  //函数参数
  this._timerArgs = args;
  // setInterval的参数
  this._repeat = isRepeat ? after : null;
  // 摧毁标记
  this._destroyed = false;

  this[kRefed] = null;

  initAsyncResource(this, 'Timeout');
}
复制代码
Timeout.prototype.unref = function() {
  if (this[kRefed]) {
    this[kRefed] = false;
    decRefCount();
  }
  return this;
};

Timeout.prototype.ref = function() {
  if (this[kRefed] === false) {
    this[kRefed] = true;
    incRefCount();
  }
  return this;
};
复制代码

在Timeout构造函数原型上添加unref,ref方法

unref方法取消setTimeout()、setInterval()...回掉函数的调用

ref方法恢复回掉函数的调用

active激活定时器对象

激活定时器对象,执行了 insert(item, true, getLibuvNow());

function active(item) {
 insert(item, true, getLibuvNow());
}
复制代码

insert

将定时器对象添加到数据结构链表内

function insert(item, refed, start) {
//取出当前延迟时间
  let msecs = item._idleTimeout;
  //判断延迟时间
  if (msecs < 0 || msecs === undefined)
    return;

  // Truncate so that accuracy of sub-milisecond timers is not assumed.
  msecs = Math.trunc(msecs);
  item._idleStart = start;

  // Use an existing list if there is one, otherwise we need to make a new one.
  //根据延迟时间生成一个链表数据结构
  var list = timerListMap[msecs];
  if (list === undefined) {
    debug('no %d list was found in insert, creating a new one', msecs);
    const expiry = start + msecs;
    timerListMap[msecs] = list = new TimersList(expiry, msecs);
    timerListQueue.insert(list);

    if (nextExpiry > expiry) {
      scheduleTimer(msecs);
      nextExpiry = expiry;
    }
  }

  if (!item[async_id_symbol] || item._destroyed) {
    item._destroyed = false;
    initAsyncResource(item, 'Timeout');
  }

  if (refed === !item[kRefed]) {
    if (refed)
      incRefCount();
    else
      decRefCount();
  }
  item[kRefed] = refed;
// 把当前timeout对象添加到对应的链表上
  L.append(list, item);
}
复制代码

append()

node定时器是在生成对应list链表头的时候开始触发的

function append(list, item) {
 if (item._idleNext || item._idlePrev) {
   remove(item);
 }

 // 处理新节点的头尾链接.
 item._idleNext = list._idleNext;
 item._idlePrev = list;

 // 处理list的前后指针指向
 list._idleNext._idlePrev = item;
 list._idleNext = item;
}
复制代码

setInterval()函数的实现与setTimeout()函数类似,只有第二个参数的处理不同

const timeout = new Timeout(callback, repeat, args, true);

// setInterval的参数

this._repeat = isRepeat ? after : null;


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Android编程权威指南(第3版)

Android编程权威指南(第3版)

比尔·菲利普斯 (Bill Phillips)、克里斯·斯图尔特 (Chris Stewart)、克莉丝汀·马西卡诺 (Kristin Marsicano) / 王明发 / 人民邮电出版社 / 2017-6 / 129.00元

Big Nerd Ranch 是美国一家专业的移动开发技术培训机构。本书主要以其Android 训练营教学课程为基础,融合了几位作者多年的心得体会,是一本完全面向实战的Android 编程权威指南。全书共36 章,详细介绍了8 个Android 应用的开发过程。通过这些精心设计的应用,读者可掌握很多重要的理论知识和开发技巧,获得宝贵的开发经验。 第3 版较之前版本增加了对数据绑定等新工具的介......一起来看看 《Android编程权威指南(第3版)》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具