這篇文章給大家分享的是有關(guān)nodejs的定時(shí)器怎么用的內(nèi)容。小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過(guò)來(lái)看看吧。
創(chuàng)新互聯(lián)建站專業(yè)為企業(yè)提供景泰網(wǎng)站建設(shè)、景泰做網(wǎng)站、景泰網(wǎng)站設(shè)計(jì)、景泰網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、景泰企業(yè)網(wǎng)站模板建站服務(wù),10余年景泰做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
看一下定時(shí)器的使用。
int main()
v_timer_t once;
uv_timer_init(uv_default_loop(), &once);
uv_timer_start(&once, once_cb, 10, 0);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
return 0;
}
我們從uv_timer_init函數(shù)開(kāi)始分析。
// 初始化uv_timer_t結(jié)構(gòu)體
int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER);
handle->timer_cb = NULL;
handle->repeat = 0;
return 0;
}
init函數(shù)和其他階段的init函數(shù)一樣,初始化handle和私有的一些字段。接著我們看start函數(shù)。該函數(shù)是啟動(dòng)一個(gè)定時(shí)器(省略部分代碼)。
// 啟動(dòng)一個(gè)計(jì)時(shí)器
int uv_timer_start(
uv_timer_t* handle,
uv_timer_cb cb,
uint64_t timeout,
uint64_t repeat
) {
uint64_t clamped_timeout;
// 重新執(zhí)行start的時(shí)候先把之前的停掉
if (uv__is_active(handle))
uv_timer_stop(handle);
// 超時(shí)時(shí)間,為絕對(duì)值
clamped_timeout = handle->loop->time + timeout;
// 初始化回調(diào),超時(shí)時(shí)間,是否重復(fù)計(jì)時(shí),賦予一個(gè)獨(dú)立無(wú)二的id
handle->timer_cb = cb;
handle->timeout = clamped_timeout;
handle->repeat = repeat;
/* start_id is the second index to be compared in uv__timer_cmp() */
handle->start_id = handle->loop->timer_counter++;
// 插入最小堆
heap_insert(timer_heap(handle->loop),
(struct heap_node*) &handle->heap_node,
timer_less_than);
// 激活該handle
uv__handle_start(handle);
return 0;
}
start函數(shù)首先初始化handle里的某些字段,包括超時(shí)回調(diào),是否重復(fù)啟動(dòng)定時(shí)器、超時(shí)的絕對(duì)時(shí)間等。接著把handle節(jié)點(diǎn)插入到最小堆中。最后給這個(gè)handle打上標(biāo)記,激活這個(gè)handle。這時(shí)候的結(jié)構(gòu)體如下。
這時(shí)候到了事件循環(huán)的timer階段。
// 找出已經(jīng)超時(shí)的節(jié)點(diǎn),并且執(zhí)行里面的回調(diào)
void uv__run_timers(uv_loop_t* loop) {
struct heap_node* heap_node;
uv_timer_t* handle;
for (;;) {
heap_node = heap_min(timer_heap(loop));
if (heap_node == NULL)
break;
handle = container_of(heap_node, uv_timer_t, heap_node);
// 如果當(dāng)前節(jié)點(diǎn)的時(shí)間大于當(dāng)前時(shí)間則返回,說(shuō)明后面的節(jié)點(diǎn)也沒(méi)有超時(shí)
if (handle->timeout > loop->time)
break;
// 移除該計(jì)時(shí)器節(jié)點(diǎn),重新插入最小堆,如果設(shè)置了repeat的話
uv_timer_stop(handle);
uv_timer_again(handle);
// 執(zhí)行超時(shí)回調(diào)
handle->timer_cb(handle);
}
}
libuv在每次事件循環(huán)開(kāi)始的時(shí)候都會(huì)緩存當(dāng)前的時(shí)間,在整個(gè)一輪的事件循環(huán)中,使用的都是這個(gè)緩存的時(shí)間。緩存了當(dāng)前最新的時(shí)間后,就執(zhí)行uv__run_timers,該函數(shù)的邏輯很明了,就是遍歷最小堆,找出當(dāng)前超時(shí)的節(jié)點(diǎn)。因?yàn)槎训男再|(zhì)是父節(jié)點(diǎn)肯定比孩子小。所以如果找到一個(gè)節(jié)點(diǎn),他沒(méi)有超時(shí),則后面的節(jié)點(diǎn)也不會(huì)超時(shí)。對(duì)于超時(shí)的節(jié)點(diǎn)就知道他的回調(diào)。執(zhí)行完回調(diào)后,還有兩個(gè)關(guān)鍵的操作。第一就是stop,第二就是again。
// 停止一個(gè)計(jì)時(shí)器
int uv_timer_stop(uv_timer_t* handle) {
if (!uv__is_active(handle))
return 0;
// 從最小堆中移除該計(jì)時(shí)器節(jié)點(diǎn)
heap_remove(timer_heap(handle->loop),
(struct heap_node*) &handle->heap_node,
timer_less_than);
// 清除激活狀態(tài)和handle的active數(shù)減一
uv__handle_stop(handle);
return 0;
}
stop的邏輯很簡(jiǎn)單,其實(shí)就是把handle從二叉堆中刪除。并且取消激活狀態(tài)。那么againt又是什么呢?again是為了支持setInterval這種場(chǎng)景。
// 重新啟動(dòng)一個(gè)計(jì)時(shí)器,需要設(shè)置repeat標(biāo)記
int uv_timer_again(uv_timer_t* handle) {
// 如果設(shè)置了repeat標(biāo)記說(shuō)明計(jì)時(shí)器是需要重復(fù)觸發(fā)的
if (handle->repeat) {
// 先把舊的計(jì)時(shí)器節(jié)點(diǎn)從最小堆中移除,然后再重新開(kāi)啟一個(gè)計(jì)時(shí)器
uv_timer_stop(handle);
uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat);
}
return 0;
}
如果handle設(shè)置了repeat標(biāo)記,則該handle在超時(shí)后,每repeat的時(shí)間后,就會(huì)繼續(xù)執(zhí)行超時(shí)回調(diào)。對(duì)于setInterval,就是超時(shí)時(shí)間是x,每x的時(shí)間后,執(zhí)行回調(diào)。這就是nodejs里定時(shí)器的底層原理。但nodejs不是每次調(diào)setTimeout的時(shí)候都往最小堆插入一個(gè)節(jié)點(diǎn)。nodejs里,只有一個(gè)關(guān)于uv_timer_s的handle。他在js層維護(hù)了一個(gè)數(shù)據(jù)結(jié)構(gòu),每次計(jì)算出最早到期的節(jié)點(diǎn),然后修改handle的超時(shí)時(shí)間。具體原理在之前的一篇文章已經(jīng)分析過(guò)。
timer階段和poll io階段也有一些聯(lián)系,因?yàn)閜oll io可能會(huì)導(dǎo)致主線程阻塞,為了保證主線程可以盡快執(zhí)行定時(shí)器的回調(diào),poll io不能一直阻塞,所以這時(shí)候,阻塞的時(shí)長(zhǎng)就是最快到期的定時(shí)器節(jié)點(diǎn)的時(shí)長(zhǎng)。
感謝各位的閱讀!關(guān)于“nodejs的定時(shí)器怎么用”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!