util.throttle(fn, limit, interval[, options])


  • fn <Function> 限制功能。
  • limit <integer> 在一个时间间隔内调用 fn 的最大次数。必须大于 0。
  • interval <integer> 每个间隔的长度(毫秒)。
  • options <Object>
    • concurrency <number> fn 最大的调用次数,其返回值可能同时处于未确定状态。必须是正整数或 Infinity。默认值: Infinity。
    • maxPending <number> 当 overflow 为 'queue' 时,允许排队的最大调用次数。必须是不负整数或 Infinity。 默认值: Infinity。
    • overflow <string> 确定超出限制的调用如何处理。 默认值: 'queue'。
      • 'queue':按照接收的顺序排队调用。
      • 'drop':立即拒接来电,不排队等待。
    • signal <AbortSignal> 一个 AbortSignal,在中止时会取消挂起的调用并阻止未来的调用。
    • strict <boolean> 当 true 时,确保在任何滚动区间内不会超过 limit。默认值: false。
  • 返回:<Function> 被节流的函数

创建一个函数,用来限制 fn 被调用的频率。默认情况下,超过限制的调用会按照接收到的顺序排队,而不是被丢弃。节流后的函数会返回一个 <Promise>,表示 fn 返回的值。如果 fn 抛出错误或返回一个被拒绝的 Promise,返回的 Promise 会带着相同的原因被拒绝。

🌐 Creates a function that limits how often fn is invoked. By default, calls that exceed the limit are queued in the order received rather than discarded. The throttled function returns a <Promise> for the value returned by fn. If fn throws or returns a rejected promise, the returned promise is rejected with the same reason.

调用只有在速率和并发容量都可用时才会开始。当 fn 开始时会消耗速率容量,而不是当调用进入队列时。并发容量在 fn 返回的值完成后释放。非 Promise 值会在下一个微任务中完成。

🌐 An invocation starts only when both rate and concurrency capacity are available. Rate capacity is consumed when fn starts, not when a call enters the queue. Concurrency capacity is released when the value returned by fn settles. Non-promise values settle during the next microtask.

当 options.overflow 为 'drop' 时,没有可用速率或并发容量的调用会被立即拒绝。当 options.overflow 为 'queue' 且 options.maxPending 个调用已经排队时,额外的调用也会被立即拒绝。当 overflow 为 'drop' 时,maxPending 没有任何效果。

🌐 When options.overflow is 'drop', calls made without available rate or concurrency capacity are rejected immediately. When options.overflow is 'queue' and options.maxPending calls are already queued, additional calls are also rejected immediately. maxPending has no effect when overflow is 'drop'.

在这两种情况下,被拒绝的调用都会返回一个被 ERR_THROTTLED 错误拒绝的 promise。这个被拒绝的 promise 会被标记为已处理,所以忽略它不会触发 'unhandledRejection' 事件。等待或显式处理这个 promise 仍然会观察到拒绝。被拒绝的调用不会消耗速率或并发容量,也不会进入队列或安排超时。

🌐 In both cases, rejected calls return a promise rejected with an ERR_THROTTLED error. The rejected promise is marked as handled, so ignoring it does not emit an 'unhandledRejection' event. Awaiting or explicitly handling the promise still observes the rejection. Rejected calls do not consume rate or concurrency capacity, enter the queue, or schedule a timeout.

默认情况下,时间间隔从新窗口中的第一次调用 fn 时开始。在该窗口期间,最多可以有 limit 次调用触发 fn。排队的调用会在每个后续窗口开始时,按最多 limit 次一组进行处理。这种窗口化的行为可能会导致调用在窗口边界时靠得很近。

🌐 By default, the interval begins when the first call in a new window invokes fn. Up to limit calls can invoke fn during that window. Queued calls are processed in groups of up to limit as each subsequent window begins. This windowed behavior can result in calls occurring close together at a window boundary.

当 options.strict 是 true 时,调用次数会被单独跟踪。这可以确保在任何滚动时间间隔内启动的调用不超过 limit 次,不过需要额外的记录工作。

🌐 When options.strict is true, invocation times are tracked individually. This ensures that no more than limit calls begin during any rolling interval, at the cost of additional bookkeeping.

如果 options.signal 被中止,挂起和将来的调用会以 AbortError 拒绝,并且信号的原因会被设置为错误的 cause,而这些调用不会调用 fn。如果信号已经被中止,throttle() 会抛出一个 AbortError。

🌐 If options.signal is aborted, pending and future calls reject with an AbortError, with the signal's reason set as the error's cause, and fn is not invoked by those calls. If the signal is already aborted, throttle() throws an AbortError.

返回的函数有以下属性:

🌐 The returned function has the following properties:

  • cancel([reason]) 会取消所有排队的调用并重置当前的限流窗口。排队的 Promise 会以 AbortError 被拒绝。如果提供了,reason 会被设置为错误的 cause。不会取消已经开始的调用。
  • hasImmediateCapacity() 会在此时调用能立即触发 fn 而不会被排队或拒绝时返回 true。这个检查不会预留容量,而且节流函数在每次调用时都会重新检测。当调用被排队以保持顺序时,它会返回 false。调用者可以通过仅在此方法返回 true 时才调用节流函数来避免创建超时。
  • pending <Promise> | <null> 是最近排队调用返回的 Promise,如果没有调用排队,则为 null。
  • pendingCount <integer> 是等待调用的调用次数。
  • activeCount <integer> 是返回值尚未确定的调用次数。
  • ref() 会让挂起和未来的超时保持 Node.js 事件循环活跃。返回节流后的函数。
  • unref() 允许在超时未结束时退出事件循环。这同样适用于未来的超时。返回节流后的函数。

已经调用过 fn 的调用不会受到 cancel() 或中止信号的影响。当调用 fn 时,它的 this 值是节流后的函数。节流函数会保留 fn 的 name 和 length。

🌐 Calls that have already invoked fn are not affected by cancel() or by an aborted signal. When invoked, fn has the throttled function as its this value. The throttled function preserves the name and length of fn.

import { throttle } from 'node:util';

const request = throttle(async (id) => {
  const response = await fetch(`https://example.com/items/${id}`);
  return response.json();
}, 2, 1_000);

// At most two requests begin during each one-second interval. All other calls
// remain queued and retain their original arguments.
const results = await Promise.all([
  request(1),
  request(2),
  request(3),
  request(4),
]);