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

@joe_777/async-relay

v0.1.0

Published

Lightweight async sequencing — concurrent work, ordered results.

Downloads

53

Readme

async-relay

轻量级异步任务编排 —— 并发执行任务,有序输出结果。

问题背景

你的网页需要按顺序(A、B、C)渲染三个模块。每个模块独立从 API 获取数据并渲染。你希望:

  • 并发获取数据 —— 不必等待 A 完成才开始 B 的数据获取
  • 有序渲染输出 —— 始终按 A → B → C 的顺序渲染,无论哪个模块的数据先返回
  • 模块完全解耦 —— A、B、C 是独立的模块,不需要相互了解

这比看起来要困难得多:

| 方案 | 并发获取数据 | 有序渲染 | 模块解耦 | |---|---|---|---| | 串行执行(依次 await) | ✗ | ✓ | ✓ | | p-queue(并发数:1) | ✗ | ✓ | ✓ | | async-mutex | ✗ | ✓ | ✓ | | Promise.all + 共享状态 | ✓ | ✓ | ✗(模块需协调) | | async-relay | | | |

p-queueasync-mutex 会序列化整个任务——如果你将获取数据+渲染作为一个整体加入队列,数据获取阶段就无法并发执行。Promise.all 虽然可以协调,但需要所有模块共享彼此的 Promise 引用,造成紧密耦合。

async-relay 采用令牌传递模型:每个模块独立获取调度器令牌,并发执行任务,然后等待自己的轮次输出结果——无需了解其他模块的存在。

安装

npm install async-relay

实际示例

三个独立模块,每个模块在独立文件中:

// moduleA.ts
import { getScheduler } from 'async-relay';

export async function loadSectionA() {
  const s = getScheduler('page-init');

  const data = await fetchDataA();   // 立即开始,与其他模块并发执行

  await s.previousComplete();         // 等待轮到我渲染
  await renderSectionA(data);
  await s.next();                     // 传递令牌给模块 B
}
// moduleB.ts
import { getScheduler } from 'async-relay';

export async function loadSectionB() {
  const s = getScheduler('page-init');

  const data = await fetchDataB();   // 与 A 和 C 并发执行

  await s.previousComplete();         // 等待 A 渲染完成
  await renderSectionB(data);
  await s.next();
}
// moduleC.ts
import { getScheduler } from 'async-relay';

export async function loadSectionC() {
  const s = getScheduler('page-init');

  const data = await fetchDataC();   // 可能最先完成数据获取,但仍需等待渲染轮次

  await s.previousComplete();         // 等待 B 渲染完成
  await renderSectionC(data);
  await s.next();
}
// page.ts —— 同时启动三个模块
import { loadSectionA } from './moduleA';
import { loadSectionB } from './moduleB';
import { loadSectionC } from './moduleC';

loadSectionA();
loadSectionB();
loadSectionC();
// 三个模块并发获取数据。
// 即使 C 的数据最先返回,渲染顺序仍是 A → B → C。
// 总耗时 ≈ max(fetchA, fetchB, fetchC),而非 fetchA + fetchB + fetchC。

使用方法

import { getScheduler, clearSchedulers } from 'async-relay';

async function task(id: string, delay: number) {
  const s = getScheduler('myQueue');

  // 主要工作并发执行
  await doWork(delay);

  // 等待前一个任务完成结果输出
  await s.previousComplete();
  await showResult(id);

  // 通知下一个任务可以开始输出结果
  await s.next();
}

// 三个任务同时启动
task('A', 10_000);
task('B', 5_000);
task('C', 0);     // C 最先完成工作,但最后输出结果

// 时间线:
// t=0s:  A、B、C 同时开始获取数据
// t=0s:  C 完成工作 —— 等待 A
// t=5s:  B 完成工作 —— 等待 A
// t=10s: A 完成工作 → 输出结果 → B 输出 → C 输出
// 总耗时:约 10s,而非 15s

clearSchedulers('myQueue');

API

getScheduler(name?: SchedulerName): Scheduler

创建并返回一个新的调度器令牌,该令牌会链接到同一 name 下先前创建的令牌。令牌按照 getScheduler 调用的顺序链接,从而确定输出顺序。

如果省略 name,将使用默认共享队列。

clearSchedulers(name?: SchedulerName): void

移除所有注册在 name 下的调度器令牌,释放内存。

interface Scheduler

interface Scheduler {
  // 当前一个任务调用 next() 后解析
  previousComplete(): Promise<void>;

  // 通知下一个任务可以开始输出阶段
  next(): Promise<void>;
}

type SchedulerName = string | symbol

许可证

MIT