npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@xdoer/timeout-interval

v1.0.2

Published

合并计数器实例,提高应用性能

Downloads

24

Readme

TimeoutInterval

合并计数器实例,提高应用性能

安装

npm -i @xdoer/timeout-interval

特点

  • 计时准确。使用 setTimeout 递归,及时修正计时误差。【首次执行,会有小于 offset 的计时误差】
  • 合并递归计时实例。在误差允许的范围内。会合并递归执行的实例,减少系统消耗

使用

下面将 setInterval 的回调时间称之为 interval, 将误差时间称之为 offset。同时为了方便描述,将底层递归 setTimeout 模拟的 setInterval 称之为 setInterval

基本使用

你可以像常规的 setIntervalclearInterval 一样使用。两次调用 setTimeoutInterval 的间隔在 500ms 的整数倍内,会合并计数器。

import { setTimeoutInterval, clearTimeoutInterval } from '@xdoer/timeout-interval';

const timerId = setTimeoutInterval(() => {
  console.log('1');
}, 1000);

clearTimeoutInterval(timerId);

上面的例子中,多次调用 setTimeoutInterval 的间隔在 500ms 的整数倍内,会合并计数器,也就是说,除了第一次的 setTimeoutInterval 调用,之后的调用,首次执行回调,最大会有 500ms 的计时误差。

如果你需要准确的计时器,你可以使用 baseSetTimeoutInterval API

import { baseSetTimeoutInterval } from '@xdoer/timeout-interval';

let timerId;

baseSetTimeoutInterval(
  () => {
    console.log('1');
  },
  1000,
  id => (timerId = id)
);

// 使用 clearTimeout 停止计时
clearTimeout(id);

高级使用

你可以通过构造函数的方式,创建实例

import { TimeoutController } from '@xdoer/timeout-interval';

// 设定 offset 误差在 1000ms
const timeoutInterval = new TimeoutController(500);

// 你可以通过 add 方法添加回调函数
const timerId = timeoutInterval.add(() => {
  console.log(1);
}, 1000);

// 通过 remove 方法停止计数器
timeoutInterval.remove(timerId);

或者你想像 setInterval 一样使用

import { TimeoutController } from '@xdoer/timeout-interval';

const timeoutInterval = new TimeoutController(1000);

export const setTimeoutInterval = timeoutInterval.add.bind(timeoutInterval);

export const clearTimeoutInterval = timeoutInterval.remove.bind(timeoutInterval);

调试

数据结构

包默认导出了一个 timeoutInterval 对象,它是一个 TimeoutController 实例 你可以打印一下,观察其数据结构.

import { timeoutInterval } from '@xdoer/timeout-interval';

console.log(timeoutInterval);

计时实例

可以通过代理劫持的方式,观察计时实例

window.setTimeout = new Proxy(window.setTimeout, {
  apply: (...args) => {
    const timerId = Reflect.apply(...args)
    console.log('setTimeout', timerId, args[2][1])
    return timerId
  }
}

原理

批量执行回调

const interval = 1000;
setInterval(() => {
  cbs.forEach(cb => cb());
}, interval);

在上面的例子的基础上,构造了 setTimeoutInterval,它的作用是向上面的 cbs 插入回调函数。调用多少次 setTimeoutInterval,就会向 cbs 插入多少个回调函数。clearTimeoutInterval 则将回调函数从 cbs 中移除。

计数器合并

let now = 0;
setInterval(() => {
  now = Date.now();
  cbs.forEach(cb => cb());
}, interval);

通过 setTimeoutInterval 向 cbs 插入回调函数时,需要根据 Date.now() - now 得到计数器目前执行的时间,在 interval 相同的情况下,只要执行时间在 offset 的范围内,就可以直接向 cbs 中插入回调函数,否则需要新的 setInterval 实例,去执行回调函数。

实际在本项目中,会根据 offset 为 interval 分配时间片段,这样的话,可以极大的减少 setInterval 实例,减小系统消耗。比如:interval 为 3000ms, offset 为 500ms,那么,无论调用多少次 setTimeoutInterval,注册多少个回调函数,都只会有 3000 / 500 = 6 个 setInterval 实例。

例子

默认合并计时误差在 500ms,也就是说在计时间隔的每一个 500ms,通过 setTimeoutInterval 插入任意数量的回调函数,都会共用一个计数实例。

下面的四个计数器中,timer1、timer2、timer4 会共用一个底层的 setInterval 实例,timer3 会用一个底层的 setInterval 实例。

import { setTimeoutInterval, clearTimeoutInterval } from '@xdoer/timeout-interval';

const timer1 = setTimeoutInterval(() => {
  console.log('1');
}, 1000);

await sleep(100);

const timer2 = setTimeoutInterval(() => {
  console.log('2');
}, 1000);

await sleep(500);

const timer3 = setTimeoutInterval(() => {
  console.log('3');
}, 1000);

await sleep(600);

const timer4 = setTimeoutInterval(() => {
  console.log('4');
}, 1000);

对于计时间隔 interval, 如果误差为 offset,则最多会产生 interval / offset 个计时实例

上面的例子中,timer1 注册了一个回调函数,计数器实例 A 开始执行,过了 100ms,timer2 开始注册回调函数,此时,计数器 A 已经执行了 100ms,检查计时误差有 500ms,因而可以直接将 timer2 的回调函数插入到计数器 A。又过了 500ms,timer3 开始注册回调函数,此时,距离计数器 A 执行已经过了 600ms,大于计时误差 500ms,因而只能新建计数器实例 B,并将 timer3 回调函数插入。又过了 600ms, timer4 注册回调函数, 此时距离计数器 A 开始执行过了 1200ms,由于每过 1000ms,开始一个计时循环,因而 1200ms 与 200ms 等效,200ms 在 500ms 的计时误差内,因而可以直接将 timer4 的回调函数插入到计数器实例 A 中。所以总共产生了两个计数器实例,相比传统的 setInterval, 大大提高了性能。