nodejs定时器setInterval,setTimeout,clearTimeout, clearInterval源码学习

前端犄角旮旯,非常规面试题总结(不断更新....) 27、判断类型的方式有哪些?1、typeof操作符:用来检测数据的类型,返回的是一个表示类型的字符串。2、instanceof操作符:用来检测对象是否为指定的构造函数的实例。3、方法:可以返回对象的准确类型。4、方法:用来判断一个变量是否为数组。(一)typeof和instanceof区别typeof返回的是一个字符串,表示变量的数据类型,可能的返回值有numberstringbooleanfunctionundefinedobject。instanceof返回的是一个布尔值,true或。 阅读详情

nodejs Timer


本文章最好配合nodejs c语言源码一起,下面几个链接是相关的写的非常好的文章。

github优化资料
alinode源码
alinode TIme
nodejs timer解析

timer.unref()的失效情况

  • 有效用法
    下面的timer1在创建后被unref, 所以不会执行callback函数;
let timer1 = setInterval(()=>{
    console.log('timer1...');
}, 1000);
timer1.unref();
  • 失效用法
    当同时存在2个timer时, unref函数失效, 两个定时器都会执行;
let timer1 = setInterval(()=>{
    console.log('timer1...');
}, 1000);
let timer2 = setInterval(()=>{
    console.log('timer2...');
}, 2000);
timer1.unref();
先看timer.unref的底层调用

unref对应的C++代码调用:

// timer_wrap.cc
constructor->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Timer"));
...
env->SetProtoMethod(constructor, "unref", HandleWrap::Unref);

即调用的是HandleWrap的Unref方法:

// handle_wrap.cc
void HandleWrap::Unref(const FunctionCallbackInfo<Value>& args) {
  HandleWrap* wrap;
  ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());

  if (IsAlive(wrap))
    uv_unref(wrap->GetHandle());
}

// uv-common.cc
void uv_unref(uv_handle_t* handle) {
  uv__handle_unref(handle);
}

uv-common.h里面uv__handle_unref干了两件事:
1) 取消REF位标记: (h)->flags &= ~UV__HANDLE_REF;
2) 如果timer处于active状态, 则将活跃句柄-1: loop->active_handle--;

对失效的解释

创建了2个timer后, node循环中, 有两个active_handles:

let timer1 = ...
// setInterval=>uv_timer_start(timer1) => active_handles = 1
let time2 = ...
// setInterval=>uv_timer_start(timer2) => active_handles = 2

active_handles>0激活loop, 这里L1为循环为标记点:

// L1: ative_handles > 0 => loop()

由于unref只是标记了REF, 这里还是执行了timeout (TODO需要说明为什么还会执行??), 当timer1到时后, 判断了active_handles=1, loop仍然处于acitve, 所以会执行再次启动定时器uv_timer_start(timer1):

// 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 L1

也就是说, 就是看loop是否active状态, 处于active, 则会执行循环中的handle, loop是否在active看active_handles是否大于0;

#define uv__has_active_handles(loop)                                          \
  ((loop)->active_handles > 0)

??? 具体为什么在unref后还会执行, 需要查验, REF标记对handle执行的影响

定时器的创建

setTimeout为入口,
1. new Timeout();
这里的Timeout对象timer时挂在TimerList链表上的node, 所以其结构如下, 其中的_idlePrev, _idleNext为链表指针:

function Timeout(after, callback, args) {
  this._called = false;
  this._idleTimeout = after;
  this._idlePrev = this;
  this._idleNext = this;
  this._idleStart = null;
  this._onTimeout = callback;
  this._timerArgs = args;
  this._repeat = null;
}
  1. active(timer)
    • 激活定时器就是将其挂在TimerList上, 调用list的insert(timer, false)函数.
    • 其实现可以看到, 这里的定时器分了两种, 分别为链表集:unrefedLists与 refedLists, setTimeout的unrefed=false, 属于unrefedLists, 这个ref具体什么意识,目前还不是很清楚;
function insert(item, unrefed) {
  const msecs = item._idleTimeout;
  ...
  item._idleStart = TimerWrap.now();
  const lists = unrefed === true ? unrefedLists : refedLists;
  var list = lists[msecs];
  if (!list) {
    lists[msecs] = list = createTimersList(msecs, unrefed);  // new TimeWrap()
  }
  L.append(list, item);
}
  1. new TimeWrap()创建C定时器
    createTimersList()时, 整个TimerList公用一个定时器, 返回给了_timer, 且指定了当前的list的超时时间:
function TimersList(msecs, unrefed) {
  ...
  this._timer = new TimerWrap();
  ...
  this.msecs = msecs;
}
  1. L.append()将当前定时器挂在链表上
    参考linkedlist.js的实现, L.append()是在链表末尾挂载定时器(相同timeout, 后挂载的超时事件越晚);

  2. 分析TimersList的创建
    下面初始化链表创建了一个TimerWrap对象, 后面的源码分析说明, 这里的_timer是一个C定时器对象

// createTimersList()
const list = new TimersList(msecs, unrefed);
// TimersList()
 this._timer = new TimerWrap();
  1. unref
    这里对引用参数进行了判断, 其底层调用了uv_unref, 如果为true, 则将该定时器标记为非REF
// createTimersList()
if (unrefed === true) list._timer.unref();
  1. 定时器启动list._timer.start(msecs)
    调用TimerWrap::Start()函数,
// TimerWrap::Start()
int err = uv_timer_start(&wrap->handle_, OnTimeout, timeout, 0);

uv_timer_start是与平台相关, 看unix实现, 下面的代码可见, unix下的定时器是以二叉树最小堆的结构来保存的, 相关原因看本页前面的几个链接.

int uv_timer_start(uv_timer_t* handle,
                   uv_timer_cb cb,
                   uint64_t timeout,
                   uint64_t repeat) {
  ...
  handle->start_id = handle->loop->timer_counter++;

  heap_insert((struct heap*) &handle->loop->timer_heap,
              (struct heap_node*) &handle->heap_node,
              timer_less_than);
  uv__handle_start(handle);
  ...
}
// uv__handle_start()
#define uv__handle_start(h) \
...
(h)->flags |= UV__HANDLE_ACTIVE;   \   // activing
if (((h)->flags & UV__HANDLE_REF) != 0) 
    uv__active_handle_add(h);  \      // refed => add()
...

heap_insrt函数定位于heap_inl.h头文件中, 实现的是一个二叉树结构的最小堆, 查找与插入复杂度均为O(logn).

  1. 定时器链表超时检查与调用回调
    下面的listOnTimeout为封装的对该TimersList的定时器的超时调用,
// createTimersList()
  list._timer[kOnTimeout] = listOnTimeout;

// listOnTimeout()
....
var now = TimerWrap.now();
...
 while (timer = L.peek(list)) {
    diff = now - timer._idleStart;
    if (diff < msecs) {
      ...
      // 剩余时间发生变化, 跟新当前定时器在定时器最小堆中的位置, 只要判断链表头的Timeout对象, 所以直接退出
      this.start(timeRemaining);
      return;
    }
    // 定时器超时
    // 链表中清除
    L.remove(timer);
    if (!timer._onTimeout) continue;
    ...
    // 执行回调
    tryOnTimeout(timer, list);
    ...
  }
  // 链表为空, 做清除工作
  assert(L.isEmpty(list));
  this.close();
  if (list._unrefed === true && list === unrefedLists[msecs]) {
    delete unrefedLists[msecs];
  } else if (list === refedLists[msecs]) {
    delete refedLists[msecs];
  }
  1. 定时器链表的清除工作
    上面的代码给出了定时器链表为空时做的清除工作:
    1) 关闭定时器对象this.close()
    该函数调用的是HandleWrap::Close(), 其中主要调用了uv_close(wrap->handle_, OnClose);, 而这个函数也是与平台相关, unix底层实现就是从最小堆中删除该定时器:
switch (handle->type) {
    ....
    case UV_TIMER:
        uv__timer_close((uv_timer_t*)handle);
        break;
    ....
}

// timer.c
void uv__timer_close(uv_timer_t* handle) {
  uv_timer_stop(handle);
}
int uv_timer_stop(uv_timer_t* handle) {
  ....
  // 从定时器堆中删除
  heap_remove((struct heap*) &handle->loop->timer_heap,
              (struct heap_node*) &handle->heap_node,
              timer_less_than);
  uv__handle_stop(handle);    // 标记为!_ACTIVE, 删引用REF
  return 0;
}

2) 释放链表的内存delete ...

TimerWrap

TimerWrap()

看看TimerWrap干了些什么, 下面的构造函数里调用了父类HandlerWrap的构造函数, 然后执行了初始化uv_timer_init:

// timer_wrap.cc
 TimerWrap(Environment* env, Local<Object> object)
      : HandleWrap(env,
                   object,
                   reinterpret_cast<uv_handle_t*>(&handle_),
                   AsyncWrap::PROVIDER_TIMERWRAP) {
    int r = uv_timer_init(env->event_loop(), &handle_);
    CHECK_EQ(r, 0);
  }

上面的HandleWrap做了一些v8环境与句柄的初始化(还没看懂), 这里的handle_是一个uv通用结构, 封装了unix和windows, 用户完成具体的事件循环:

uv_timer_t handle_;

其又包含(继承)通用结构, 该结构中有data与loop对象:

#define UV_HANDLE_FIELDS            \
  /* public */                      \
  void* data;                       \
  /* read-only */                   \
  uv_loop_t* loop;                  \
  uv_handle_type type;              \ 
  ...

loop对象的结构如下, 具体的结构与平台的回调库相关, 这里我们注意两个公共的数据: data(用户定义的数据, 即后面的handle_->data = this;)与active_handlers(消息循环引用计数器),

struct uv_loop_s {
  /* User data - use this for whatever. */
  void* data;
  /* Loop reference counting. */
  unsigned int active_handles;
  ...
};

具体的东西很底层, 没看;

再重点看下TimerWrap里的uv_timer_init

这里的handler_可以理解为定时器句柄.
uv_timer_init具体实现与平台相关,

// timer_warp.cc TimerWrap构造函数
int r = uv_timer_init(env->event_loop(), &handle_);
CHECK_EQ(r, 0);

看下unix的实现

// uv/src/unix/timer.c
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;
}

调用了通过的handle_init, 主要就是执行了写初始化, 并标记为REF状态.

#define uv__handle_init(loop_, h, type_)             \
  do {                                               \
    (h)->loop = (loop_);                             \
    (h)->type = (type_);                             \
    (h)->flags = UV__HANDLE_REF;  /* Ref the loop when active. */       \   
    ....
  }                                                  \
  while (0)

显然, setTimeout函数的unrefed=false, 所以属于引用类型, node内部使用的_unrefActive()属于unrefed=true;


总结

TimerWrap就是构造了一个平台相关的C定时器.

setInterval定时器

  1. 其实现与setTimeout一样, 只是创建Timeout对象后, 设置了timer._repeat = repeat;
  2. 在执行回调函数后, 判断了重复
// ontimeout
if (timer._repeat)
    rearm(timer);

在rearm实现里, 就是重写开始计时, 这里的_handle可能不是Timeout实例(???_unrefAcitve()??)

// rearm
function rearm(timer) {
  // // Do not re-arm unenroll'd or closed timers.
  if (timer._idleTimeout === -1) return;
  // If timer is unref'd (or was - it's permanently removed from the list.)
  if (timer._handle && timer instanceof Timeout) {
    timer._handle.start(timer._repeat);
  } else {
    timer._idleTimeout = timer._repeat;
    active(timer);
  }
}

clearTimeout, clearInterval

clearTimeout实现如下, clearInterval时对clearTimeout的封装, 仅仅多了置_repeat=null, 下面的unenroll好像时内部对就重复利用的定时器, 现在还没看到:

exports.clearTimeout = function(timer) {
  if (timer && (timer[kOnTimeout] || timer._onTimeout)) {
    timer[kOnTimeout] = timer._onTimeout = null;
    if (timer instanceof Timeout) {
      timer.close(); // for after === 0
    } else {
      unenroll(timer);    // ????????
    }
  }
};

setImmediate

  • 全局只有条立即执行队列
    该队列为一单链表, 链接的节点是new Immediate()对象
// Create a single linked list instance only once at startup
var immediateQueue = new ImmediateList();

function ImmediateList() {
  this.head = null;
  this.tail = null;
}
  • setImmediate(cb)函数做了3件事:
    1. 实例化Immediate对象,并初始化;
    2. 将对象挂载到全局的立即执行回调队列immediateQueue;
    3. 同时, 对全局process对象挂载立即执行回调函数_immediateCallback
function createImmediate(args, callback) {
  var immediate = new Immediate();
  immediate._callback = callback;
  immediate._argv = args;
  immediate._onImmediate = callback;

  if (!process._needImmediateCallback) {
    process._needImmediateCallback = true;
    process._immediateCallback = processImmediate;
  }
  immediateQueue.append(immediate);
  return immediate;
}
  • 全局process._immediateCallback调用
function processImmediate() {
  var immediate = immediateQueue.head;
  var tail = immediateQueue.tail;
  var domain;

  // 立即执行链表只会调用一次, 所以,这里提前删除, 以防在callback执行期间又创建了一个
  immediateQueue.head = immediateQueue.tail = null;

  // 遍历执行链表
  while (immediate) {
    ...
    immediate._callback = immediate._onImmediate;
    // Save next in case `clearImmediate(immediate)` is called from callback
    var next = immediate._idleNext;
    tryOnImmediate(immediate, tail);
    ....
    // If `clearImmediate(immediate)` wasn't called from the callback, use the `immediate`'s next item  这里的判断好像用处不大
    if (immediate._idleNext)
      immediate = immediate._idleNext;
    else
      immediate = next;
  }
  ...  //后面这几句没看懂
}
  • Immediate回调函数执行
    回调函数实在try{}catch{}下执行, 如果抛出异常, 仍然会在下一帧继续执行后面的回调函数:
function tryOnImmediate(immediate, oldTail) {
  var threw = true;
  try {
    runCallback(immediate);
    threw = false;
  } finally {
    if (threw && immediate._idleNext) {
      // Handle any remaining on next tick, assuming we're still alive to do so.
      ...
      const next = immediate._idleNext;
      if (curHead) {
        ...
      } else {
        immediateQueue.head = next;
        immediateQueue.tail = oldTail;
      }
      process.nextTick(processImmediate);
    }
  }
}

node timer触发

在回调主循环uv_run里面, 每帧一开始就会处理定时器:

// unix.c 
int uv_run(uv_loop_t* loop, uv_run_mode mode) {
    while (r != 0 && loop->stop_flag == 0) {
        uv__update_time(loop);
        uv__run_timers(loop);
        ....
        r = uv__loop_alive(loop);
    }
}

uv_run_timers

uv_run_timers的实现如下, 就是不断从最小堆中去除根节点, 判断是否超时, 如果没超时则退出:

void uv__run_timers(uv_loop_t* loop) {
  struct heap_node* heap_node;
  uv_timer_t* handle;

  for (;;) {
    heap_node = heap_min((struct heap*) &loop->timer_heap);
    if (heap_node == NULL)
      break;

    handle = container_of(heap_node, uv_timer_t, heap_node);
    if (handle->timeout > loop->time)
      break;

    uv_timer_stop(handle);
    uv_timer_again(handle);
    handle->timer_cb(handle);
  }
}

而如果超时, 则上面调用了uv_timer_stop来停止定时器与uv_timer_again来从启定时器.
下面时这两个函数的实现:
* uv_timer_stop
1) 取得定时器, 从最小堆中移除;
2) 标记~ACTIVE, 并减计数器;

// uv/src/unix/timer.c
int uv_timer_stop(uv_timer_t* handle) {
  if (!uv__is_active(handle))
    return 0;

  heap_remove((struct heap*) &handle->loop->timer_heap,
              (struct heap_node*) &handle->heap_node,
              timer_less_than);
  uv__handle_stop(handle);

  return 0;
}
  • uv_timer_again
    如果定时器要求重复触发(repeat), 则先停止, 再从新启动(跟新超时时间, 插入heap)
int uv_timer_again(uv_timer_t* handle) {
  if (handle->timer_cb == NULL)
    return -EINVAL;
  if (handle->repeat) {
    // 先移除
    uv_timer_stop(handle);
    // 再从新启动
    uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat);
  }
  return 0;
}

事件循环结束


  • 上面uv_run里面, 循环继续的条件是uv__loop_alive(loop) != 0 && loop->stop_flag == 0, 即要么loop被标记为STOP状态, 要么uv__loop_alive返回为0;
  • 而uv__loop_alive的实现又说明, 在loop里的活跃句柄active_handles对象数只要大于0就不会退出循环, 这就是本文前面的例子里解释;

前面的例子定义了2个 setInterval对象, unref()了一个, 此时 loop->active_handles=1, 所以循环会继续, 在执行 uv__run_timers时依次执行 uv_timer_stop和 uv_timer_again, 由于两个定时器都有 repeat==true, 所以两个定时器会再次执行 uv_timer_start, 此时没有被 unref的定时器再一次会执行 uv__active_handle_add(), 所以循环会一直持续下去;
// uv/src/unix/timer.c
static int uv__loop_alive(const uv_loop_t* loop) {
  return uv__has_active_handles(loop) ||
         uv__has_active_reqs(loop) ||
         loop->closing_handles != NULL;
}
// uv-common.h
#define uv__has_active_handles(loop)   \
  ((loop)->active_handles > 0)

总结
unref仅仅时将定时器对象句柄解引用, 不是将定时器从定时器链表中移除, 所以如果循环没有结束, 则定时器还是会被调用;

Timer总结

  1. let timer = setTimeout(cb, t);函数返回的不是一个真的定时器对象, 而是一个js对象Timeout, 该对象是TimersList链表的节点类型, 这里我称它为虚定时器;
  2. TimersList身上的_timer才是真正的C定时器对象, 每个list上的所有虚定时器共享这个_timer, 同一个链表上的所有虚定时器有相同的超时时间;
  3. 链表本身是由list节点链接首尾节点组成的闭合链表, 检查超时每次只要取头部虚定时器, 添加时只要在尾部添加, 时间复杂度都为O(1);
  4. unix定时器对象在底层的存储方式为最小堆结构, 增删查定时器复杂度O(logn);
快速掌握Node.js中setTimeout和setInterval的使用方法 Node.js和js一样也有计时器,超时计时器、间隔计时器、及时计时器,它们以及process.nextTick(callback)函数来实现事件调度。今天先学下setTimeout和setInterval的使用。 一、setTimeout超时计时器(和GCD中的after类似) 在node.js中可以使用node.js内置的setTimeout(callback,delayMillSeconds,[args])方法。当调用setTime()时回调函数会在delayMillSeconds后 执行.setTime() 会返回一个定时器对象ID,可以在delayMillSeconds到期前将ID传 立即下载

相关推荐

【卫星语音通信】神经网络语音编解码算法:AudioDec

最新研究表明,通过深度学习模型对语音信号进行智能压缩与重建,可在12 kbps码率下实现48 kHz采样率的高保真语音还原,其主观音质评分(MOS)可达3.9分(满分4.27分),远超传统参数编码技术。这意味着未来卫星电话的通话质量有望达到CD级水准,而蜂窝网络的语音业务带宽需求可降低60%以上,为蜂窝和卫星网络中的大规模物联网语音交互铺平道路。它的诞生为神经音频编解码技术带来了一次重要升级,有助于让未来实时语音交互更流畅、更清晰,也让我们进一步体验到人工智能与音频信号处理结合所带来的创新可能性。

u011376987的博客 1472

Nodejs——setInterval循环器使用

一个函数需要后台不断的循环调用,可以使用setInterval函数 setInterval(func, delaytime, param1, param2, ...); 1、myInterval是一个定时器对象,无限循环 var startTime = Date.now() var myfunction =function(startTime){ var timeNow = Dat...

tellmewsm 8452

Setup&Hold互卡问题和Useful Skew的影响

setup和hold互卡问题分析

godping的博客 5093

如何在Node.js中使用定时器

在Node.js中使用定时器是一项常见且重要的任务,特别是在需要执行定时任务或者轮询操作的情况下。Node.js提供了多种方式来实现定时器功能,包括`setTimeout`、`setInterval`和`setImmediate`等方法

有我更精彩的博客 1865

ST电机库实战:从状态机到稳健启动流程的深度解析

本文深度解析了ST电机库中状态机的核心作用与稳健启动流程。状态机作为电机控制的“交通指挥官”,通过IDLE、CHARGE_BOOT_CAP、ALIGNMENT等一系列状态的严格跳转,确保了永磁同步电机(PMSM)和直流无刷电机(BLDC)的安全启动与可靠运行,是避免硬件损坏和实现平稳控制的关键。

weixin_29161785的博客 226

Vmware虚拟机一打开就蓝屏解决方法

Vmware虚拟机一打开就蓝屏解决方法 问题: 一打开Vmware虚拟机就蓝屏,如下图所示: 解决方法1: 启用和关闭windows功能(对话框)——>虚拟机平台(选项打对勾,默认没勾选)——>确定后重启——>再进应该就好了。 解决方法2: ①.查看电脑中的Hyper-V服务是否关闭 1.打开控制面板-程序-程序和功能 2.双击启用或关闭Windows功能 3.把Hyper-V前面的勾去掉 如果后期发下没有反应,或者电脑本身Hyper-V的服务没有打开,可以先打开在关闭,试

m0_55901666的博客 26万+

nodejs 事件循环

node.js的事件的循环

wrc的专栏 1149

2024年11月架构设计师案例分析真题回顾,附参考答案、解析及所涉知识点(二)

软考高级系统架构设计师考试包含三个科目:信息系统综合知识、案例分析和系统架构设计论文。考试形式为机考。本文主要回顾2024年下半年(2024-11-10)系统架构设计师考试上午第二科案例分析的题目,同时附带参考解答、解析和所涉知识点。

一几文的编程学习笔记。 1660

nodejs 定时任务的执行 node-cron node-schedule

使用第三方库node-schedule,就可以轻松解决,实现各种复杂的任务调动。也可使用第三方库node-cron来解决简单的任务调动

阿坤的博客 2170

061、LQR(线性二次型调节器)控制基础

本文介绍了LQR(线性二次型调节器)控制在飞控系统中的应用。作者通过一次四旋翼炸机事故引出PID控制的局限性,详细讲解了LQR的数学原理、权重矩阵的物理意义以及实际工程实现方法。文章对比了LQR与PID的控制效果,指出LQR在鲁棒性方面的优势,同时也分析了其存在稳态误差等缺点。最后总结了LQR在飞控中的适用场景,建议将LQR与PID结合使用,并预告了下一章将讨论带积分项的LQI控制。全文包含具体案例、代码实现和调参经验,为飞控算法开发者提供了实用指导。

2401_88858637的博客 720

05 - matlab m_map地学绘图工具基础函数 - 设置比例尺指北针

本篇介绍下m_map中添加指北针(m_northarrow)、比例尺(m_ruler)和进行比例缩放(m_scale)的函数及其用法 。提供了 示例以便参考,罗列了 各函数可设属性。希望对绘图的你有所帮助。

weixin_43339605的博客 1946

setInterval的启动与停止,以及node-schedule的简单使用

setInterval 简介      setInterval()方法可按照指定的周期来调用函数或者计算表达式(以毫秒为单位) setInterval 启动 语法 :setInterval(函数表达式,毫秒数); 如:间隔十秒打印一次 test setInterval(function () { console.log('test') }, 1000) setInterval 停止 语法:clearInterval(interval) //定时

不懂一休 1万+

YOLOv11改进 | Neck篇 | 轻量化跨尺度跨通道融合颈部CCFM助力YOLOv11有效涨点

到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv11改进有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,目前本专栏免费阅读(暂时,大家尽早关注不迷路~)如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~YOLOv11有效涨点专栏——本专栏持续复习各种顶会内容——科研必备​ycb。

Snu77的博客 8079

TanStack Query 定时器统一管理:TimeoutManager 源码解析与自定义 TimeoutProvider 实战指南

TanStack Query(本仓库 `query-core` 即其核心实现)内部大量依赖 `setTimeout` / `setInterval` 定时器来实现 `staleTime`、`gcTime`、重试(retry)、节流(throttle)与防抖(debounce)等机制。本篇以 [TimeoutManager 参考文档](https://link.gitcode.com/i/94e47

gitblog_00072的博客 872

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Node.js 全局对象

前言 JavaScript 中,window 是全局对象, 而 Node.js 中, global是全局对象,其他的所有的全局变量,都是 global 对象的属性。 在Node.js 中,使用global对象时,无需引用,直接使用即可,因为它存在于最外层的上下文环境中。 一、__filename __filename表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。 二、__dirname ...

yuanlnet的博客 315

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Vitest 日期 Mock 完整指南:用 vi.setSystemTime 与 fake timers 掌控测试时间

在编写单元测试时,凡是依赖“当前时间”的业务逻辑(如营业时间校验、优惠券过期、倒计时、报表统计)都会面临同一个难题:测试结果随真实时钟漂移,今天能通过明天就挂。Vitest 提供了一套基于 `@sinonjs/fake-timers` 的时间模拟体系,让你可以在测试中完全掌控系统日期——从 `new Date()`、`Date.now()` 到 `performance.now()` 都能被冻结或

gitblog_00701的博客 261

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

树莓派Node.js GPIO控制:从环境配置到LED状态指示系统实战

GPIO(通用输入输出)是嵌入式系统和物联网设备与物理世界交互的基础接口,它允许微控制器通过数字信号控制外部硬件。在树莓派等单板计算机上,通过编程控制GPIO引脚,可以实现对LED、传感器等外设的驱动,这是物联网和硬件原型开发的核心技术。Node.js凭借其异步非阻塞I/O模型和丰富的npm生态,为树莓派GPIO编程提供了高效的开发体验,特别适合需要处理并发事件或快速迭代的硬件项目。在工程实践中,使用onoff等库可以简化GPIO操作,但环境配置和权限管理是关键环节,例如在树莓派上安装Node.js时,需通

weixin_34221773的博客 696

DSHCode沙箱隔离原理深潜:bwrap/Landlock、Seatbelt与Windows ACL三大后端对比

DSHCode 是 DeepSeek Harness 的社区桌面伴侣应用,它的 Agent 需要执行 bash 命令、读写文件,而这些操作默认全部运行在**进程沙箱**之中。本文带你深潜 DSHCode 沙箱隔离原理:逐层拆解 Linux 的 bwrap 与 Landlock、macOS 的 Seatbelt、Windows 的 ACL 受限令牌三大后端,看懂它如何保证"命令绝不可能静默逃逸"。

gitblog_01197的博客 944

node&npm&nrm&nvm

Node.js 是一个能够在服务器端运行 JavaScript 的开放源代码、跨平台 JavaScript 运行环境。Node.js 由 Node.js 基金会持有和维护,并与 Linux 基金会有合作关系。Node.js 采用 Google 开发的 V8 运行代码,使用事件驱动、非阻塞和异步输入输出模型等技术来提高性能,可优化应用程序的传输量和规模。这些技术通常用于数据密集的即时应用程序。Node.js 大部分基本模块都用 JavaScript 语言编写。

pscool的博客 1678

2026面试预判及实际面试

本文摘要总结了多个前端开发岗位的面试问题和答案,涵盖Vue3、ECharts、数据可视化、JavaScript核心概念等内容。 Vue3高频问题 组合式API相比选项式API的优势在于逻辑内聚、更好的TS支持和复用性。 状态管理推荐Pinia,轻量且支持组合式API风格。 动态路由和权限控制通过router.addRoute和路由守卫实现。 响应式原理基于Proxy和Reflect,解决了Vue2的局限性。 ECharts与数据可视化 性能优化包括数据聚合、懒渲染和dataZoom区域缩放。

Mr.小灰狼_随笔 579

Nodejs学习笔记(二)——模块

文章目录一 、模块基础(1)引入和导出(2)局部变量二、模块分类三、 URL模块练习:四、 querystring模块五、 timer模块(1)一次性定时器(2)周期性定时器(3)立即执行定时器六、文件系统模块(fs) 一 、模块基础 (1)引入和导出 是一个独立的功能体,模块可以被其它的模块引入,也可以引入其它的模块 require() 用于引入其它的模块 module.exports 是一个空对象,用于存放导出的内容 module_exports.js console.log("emodu

Erya Programming learning 676

15、计时器方法1(setInterval、clearInterval)

1、创建计时器(setInterval) 2、取消计时器(clearInterval)

weixin_45065754的博客 1298

Node.js 事件循环:定时任务、延迟任务和 I/O 事件的艺术

Promise 和 Async/Await 是 JavaScript 中的异步编程解决方案,它们与事件循环密切相关。Promise 是一种用于处理异步操作结果的对象。它可以表示一个异步操作的最终完成(fulfillment)或失败(rejection)。Promise 对象有一个then方法,用于注册在异步操作完成后执行的回调函数。在事件循环中,Promise 的处理通过 Promise 队列(promise)进行。当创建一个 Promise 对象时,它会被添加到 Promise 队列中。事件循环会在。

你若盛开,清风自来 2775

百字系列--NodeJS中的setInterval 与 setTimeout 差异

在 NodeJs 中 setInterval 与 setTimeout 虽然参数相同,但是他俩还是有差异的。 setInterval 每隔一段时间执行一次,循环执行,间隔时间是固定的,不受回调函数执行时间的影响。setTimeout 经过一定时间再执行,仅执行一次,如果再次执行,执行时间等于回调函数执行时间加上我们设定的间隔时间。 全篇99个汉字,阅读时长:30秒 ...

喵叔 958

setTimeout() 与 setInterval() 的源码分析

今天被问到setTimeout() 和 setInterval(),以为这只是简单的计时器,结果几番查找之后发现这里面大有乾坤,下面是传送门 setTimeout() 与 setInterval() 的源码分析 你所不知道的setTimeout 你所不知道的setInterval ...

Sixology的博客 599

nodeJS笔记 | 定时器

Node的四个定时器:setTimeout(),setInterval(),setImmediate(),process.nextTick() 1.setTimeout() 一次性定时器setTimeout(), 官方中文文档: setTimeout() 用于设定在指定的毫秒数之后执行某些代码. 这个函数和浏览器中的JavaScript API window.setTimeout() 类似, 然...

weixin_44290930的博客 1025
上一篇: sublime text3配置
下一篇: ubuntu新帐号登陆命令行只有$,回车键失效为^H
呵离
博客等级 码龄12年 18粉丝 86原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值