Node.js 中的定時器函數與web瀏覽器中的定時函數API 相似,增長了一個setImmediate() 函數,它們向任務隊列添加定時任務node
setTimeout(callback, delay) delay毫秒後執行回掉函數
setInterval(callback,delay) 每隔delay毫秒執行一次回掉函數
setImmediate() 將在當前事件輪詢的末尾處執行。同步任務執行完後,delay不爲0時首先當即執行setImmediate() 函數
web
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服務器端返回響應頭的這段時間, 若是超出設定時間,就終止請求bash
lib/timers.js
服務器
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;
}
複製代碼
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方法恢復回掉函數的調用app
激活定時器對象,執行了 insert(item, true, getLibuvNow());async
function active(item) {
insert(item, true, getLibuvNow());
}
複製代碼
將定時器對象添加到數據結構鏈表內tcp
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);
}
複製代碼
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;
function clearTimeout(timer) {
if (timer && timer._onTimeout) {
timer._onTimeout = null;
unenroll(timer);
}
}
複製代碼
移除定時器,不進行函數跟蹤
以上是我的淺顯的理解,刨的還不夠深,道阻且長