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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@siroi/async-scheduler-task

v1.0.0

Published

任务调度队列

Readme

@siroi/async-scheduler-task

npm version

AsyncScheduler 是一个高性能异步时间分片调度器,专为解决 JavaScript 主线程阻塞问题设计。它通过优先级队列和零延迟分片技术,确保高优先级任务(如用户交互)优先执行,同时将耗时操作拆分为小片段在空闲时间执行,从而保持应用流畅性。

核心特性

帧级精准控制:默认每帧最多执行 16ms(60fps),避免阻塞浏览器渲染
🎯 多级别优先级:4 级优先级调度,保障用户交互(滚动/拖拽)优先执行
🚀 零延迟分片:基于 MessageChannel 实现主线程让出,比 setTimeout 快 3-5 倍
🛡️ 安全降级:MessageChannel 失效时自动回退到 setTimeout 兜底
🔧 灵活控制:支持任务取消、队列清空、强制刷新等精细化操作
📦 类型安全:完整 TypeScript 类型定义,适配现代前端工程化
🌱 轻量无冗余:仅依赖 @siroi/min-heap,无其他第三方依赖

适用场景

  • 虚拟滚动列表(大数据量渲染)
  • 复杂数据计算/转换(如 Excel 解析、大数据可视化)
  • 高频率用户交互优化(滚动、拖拽、输入响应)
  • 非关键任务延迟执行(预加载、埋点分析、缓存更新)
  • 避免长任务导致的页面卡顿/无响应(Long Task 优化)

安装方法

使用 npm:

npm install @siroi/async-scheduler-task

使用 yarn:

yarn add @siroi/async-scheduler-task

使用 pnpm:

pnpm add @siroi/async-scheduler-task

快速开始

基础用法

import { AsyncScheduler, TaskPriority } from '@siroi/async-scheduler-task';

// 创建调度器实例
const scheduler = new AsyncScheduler();

// 高优先级任务(滚动时触发)
scheduler.schedule(() => updateScrollPosition(), TaskPriority.IMMEDIATE);

// 普通任务
const taskId = scheduler.schedule(() => processLargeData());

// 取消任务(如果需要)
scheduler.cancelTask(taskId);

// 组件卸载时清理
scheduler.destroy();

自定义配置(低端设备优化)

// 为低端设备调整配置
const scheduler = new AsyncScheduler({
  frameTime: 25,      // 降低帧率至40fps,增加每帧执行时间
  yieldInterval: 10   // 延长降级方案的分片间隔
});

API 文档

TaskPriority 枚举

任务优先级枚举,数值越小优先级越高。注意:不要随意增加新优先级级别,避免破坏调度平衡

| 优先级 | 数值 | 适用场景 | 特性 | |----------|------|------------------------|----------------------------------------| | IMMEDIATE| 0 | 滚动事件、关键用户交互 | 最高优先级,会中断当前执行帧,立即调度 | | HIGH | 1 | 拖选操作、输入响应 | 高优先级,当前帧内执行,不会中断IMMEDIATE任务 | | NORMAL | 2 | 常规数据处理、非关键更新 | 默认级别,在高优任务完成后执行 | | LOW | 3 | 预加载、分析埋点、缓存更新 | 低优先级,仅当没有其他任务时执行 |

AsyncScheduler 类

构造函数

new AsyncScheduler(options?: { frameTime?: number; yieldInterval?: number })

创建调度器实例。

参数:

  • options.frameTime - 每帧最大执行时间(ms),默认 16ms(60fps)
  • options.yieldInterval - 降级方案的分片间隔(ms),默认 5ms

示例:

// 标准配置
const scheduler = new AsyncScheduler();

// 低端设备优化配置
const lowEndScheduler = new AsyncScheduler({
  frameTime: 25,     // 40fps
  yieldInterval: 10
});

schedule

schedule(fn: () => void, priority: TaskPriority = TaskPriority.NORMAL): number

提交任务到调度器。

执行顺序规则:

  1. 优先级高的先执行(IMMEDIATE > HIGH > NORMAL > LOW)
  2. 同优先级按提交顺序执行(FIFO)

参数:

  • fn - 任务执行函数(无参无返回值)
  • priority - 任务优先级,默认 NORMAL

返回值: 任务唯一 ID(可用于取消)

示例:

// 高优先级任务
const highTaskId = scheduler.schedule(() => {
  console.log('处理拖选操作');
}, TaskPriority.HIGH);

// 普通优先级任务(默认)
const normalTaskId = scheduler.schedule(() => {
  console.log('处理常规数据');
});

cancelTask

cancelTask(taskId: number): boolean

取消指定 ID 的任务。

注意事项:

  • 任务可能已在执行中(无法中断正在执行的函数)
  • 仅能取消队列中等待执行的任务
  • 返回 false 表示任务不存在或已被执行/取消

参数:

  • taskId - 通过 schedule 返回的任务 ID

返回值: 是否成功取消

示例:

const taskId = scheduler.schedule(() => processData());

// 取消任务
const isCancelled = scheduler.cancelTask(taskId);
console.log('任务是否取消成功:', isCancelled);

clearQueue

clearQueue(): void

清空调度器队列。

执行效果:

  • 丢弃所有未执行任务
  • 重置 yield 状态
  • 不会中断正在执行的任务

适用场景:

  • 组件卸载前清理
  • 路由切换时重置

示例:

// 组件卸载时
componentWillUnmount() {
  scheduler.clearQueue();
}

flushQueue

flushQueue(): void

立即同步执行所有剩余任务。

重要警告:

  • 会阻塞主线程直到所有任务完成
  • 破坏分片机制,仅用于极端场景
  • 执行后自动清空队列并重置状态

典型场景:

  • 应用卸载前的强制清理
  • 测试环境同步验证

示例:

// 应用退出前确保所有任务完成
window.addEventListener('beforeunload', () => {
  scheduler.flushQueue();
});

destroy

destroy(): void

销毁调度器实例。

必须执行的清理操作:

  1. 清空任务队列
  2. 关闭 MessageChannel 端口(防止内存泄漏)
  3. 重置内部状态

重要: 在组件卸载/页面关闭前调用,否则可能导致端口泄漏、内存泄漏或意外任务执行。

示例:

// React组件卸载时
useEffect(() => {
  const scheduler = new AsyncScheduler();

  return () => {
    scheduler.destroy(); // 组件卸载时销毁调度器
  };
}, []);

任务优先级说明

优先级使用指南

| 优先级 | 使用场景示例 | 执行特性 | 注意事项 | |----------|------------------------|------------------------|------------------------------| | IMMEDIATE| 滚动位置更新、窗口大小调整 | 立即中断当前帧执行 | 避免长时间运行,应保持轻量 | | HIGH | 拖拽操作、输入框实时验证 | 当前帧内优先执行 | 控制执行时间在5ms内 | | NORMAL | 列表数据处理、UI非关键更新 | 按顺序执行 | 可包含稍复杂逻辑 | | LOW | 数据预加载、日志上报、缓存更新 | 仅在空闲时执行 | 可被高优先级任务抢占 |

优先级最佳实践

  1. 避免滥用高优先级:IMMEDIATE 和 HIGH 优先级应仅用于用户直接交互的场景,过多高优先级任务会导致普通任务饥饿

  2. 合理拆分大任务:对于超过 10ms 的操作,即使是高优先级也应拆分为多个任务

  3. 优先级递进使用:同一系列操作应使用不同优先级,如:

    • 用户点击(IMMEDIATE)→ 数据验证(HIGH)→ 数据处理(NORMAL)→ 日志上报(LOW)

性能优化和降级策略

性能优化配置

设备适配

根据设备性能调整参数:

// 检测设备性能并动态调整
const isLowEndDevice = detectLowEndDevice(); // 自定义设备检测函数

const scheduler = new AsyncScheduler({
  frameTime: isLowEndDevice ? 25 : 16,    // 低端设备降低帧率
  yieldInterval: isLowEndDevice ? 10 : 5  // 低端设备延长降级间隔
});

任务拆分策略

将大型任务拆分为小型任务:

// 不佳:单个大任务
scheduler.schedule(() => {
  for (let i = 0; i < 10000; i++) {
    processItem(data[i]); // 可能阻塞主线程
  }
}, TaskPriority.NORMAL);

// 优化:拆分为多个小任务
function processInChunks(data, chunkSize = 100) {
  let index = 0;

  function processChunk() {
    const end = Math.min(index + chunkSize, data.length);
    for (; index < end; index++) {
      processItem(data[index]);
    }

    if (index < data.length) {
      // 继续调度下一个分片
      scheduler.schedule(processChunk, TaskPriority.NORMAL);
    }
  }

  scheduler.schedule(processChunk, TaskPriority.NORMAL);
}

processInChunks(largeDataset);

降级策略详解

AsyncScheduler 采用双重保障策略确保分片可靠性:

主方案:MessageChannel

  • 通过 port2.postMessage 触发 port1.onmessage
  • 在当前任务队列清空后立即执行(微任务之后)
  • 延迟≈0ms(比 setTimeout 快 3-5 倍)

降级方案:setTimeout

  • 当 MessageChannel 未触发时(罕见情况)
  • 使用 yieldInterval(5ms) 作为兜底

性能对比

| 机制 | 平均延迟 | 稳定性 | 兼容性 | |----------------|----------|----------|------------| | MessageChannel | 0.1ms | ★★★★★ | 现代浏览器 | | setTimeout(0) | 4-8ms | ★★★☆☆ | 全平台 | | setTimeout(5) | 5-10ms | ★★★★☆ | 全平台 |

注意事项和最佳实践

使用注意事项

  1. 任务函数纯度:任务函数应避免修改共享状态,或确保线程安全

  2. 错误处理:任务函数内部应包含错误处理,调度器会捕获并打印错误但不会中断其他任务

// 推荐的任务错误处理
scheduler.schedule(() => {
  try {
    riskyOperation();
  } catch (e) {
    // 业务错误处理
    handleError(e);
  }
}, TaskPriority.NORMAL);
  1. 任务执行时间:单个任务执行时间应控制在 frameTime(16ms) 以内,过长会导致卡顿

  2. 避免循环依赖:任务函数不应直接调度自身,可能导致无限循环

最佳实践

组件中使用

在 React/Vue 等组件化框架中使用时,应在组件卸载时清理:

// React Hook 示例
import { useRef, useEffect } from 'react';
import { AsyncScheduler } from '@siroi/async-scheduler-task';

function DataTable() {
  const schedulerRef = useRef<AsyncScheduler | null>(null);

  useEffect(() => {
    // 创建调度器
    schedulerRef.current = new AsyncScheduler();

    // 组件卸载时清理
    return () => {
      schedulerRef.current?.destroy();
      schedulerRef.current = null;
    };
  }, []);

  // 使用调度器处理数据
  const loadLargeData = (data) => {
    schedulerRef.current?.schedule(() => {
      processAndRenderData(data);
    }, TaskPriority.NORMAL);
  };

  return <div>Data Table</div>;
}

虚拟滚动实现

结合虚拟滚动的典型应用:

function renderVirtualList(items) {
  const scheduler = new AsyncScheduler();
  const container = document.getElementById('list-container');
  let visibleRange = getVisibleRange();

  // 监听滚动事件(高优先级)
  container.addEventListener('scroll', () => {
    const newRange = getVisibleRange();
    if (newRange !== visibleRange) {
      visibleRange = newRange;
      // 立即更新可见区域
      scheduler.schedule(() => updateVisibleItems(visibleRange), TaskPriority.IMMEDIATE);
    }
  });

  // 预加载(低优先级)
  scheduler.schedule(() => preloadItems(visibleRange.expand(5)), TaskPriority.LOW);
}

大数据处理

处理大量数据时的优化策略:

function processLargeDataset(dataset) {
  const scheduler = new AsyncScheduler();
  const batchSize = 100; // 每批处理数量
  let index = 0;

  function processBatch() {
    const end = Math.min(index + batchSize, dataset.length);

    // 处理当前批次
    for (; index < end; index++) {
      processItem(dataset[index]);
    }

    // 更新进度(高优先级)
    scheduler.schedule(() => {
      updateProgress((index / dataset.length) * 100);
    }, TaskPriority.HIGH);

    // 如果还有数据,继续调度下一批
    if (index < dataset.length) {
      scheduler.schedule(processBatch, TaskPriority.NORMAL);
    } else {
      // 全部完成(高优先级)
      scheduler.schedule(() => {
        showCompletionMessage();
      }, TaskPriority.HIGH);
    }
  }

  // 开始处理第一批
  scheduler.schedule(processBatch, TaskPriority.NORMAL);
}