fix
This commit is contained in:
423
book/node_modules/undici/lib/util/timers.js
generated
vendored
Normal file
423
book/node_modules/undici/lib/util/timers.js
generated
vendored
Normal file
@ -0,0 +1,423 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* This module offers an optimized timer implementation designed for scenarios
|
||||
* where high precision is not critical.
|
||||
*
|
||||
* The timer achieves faster performance by using a low-resolution approach,
|
||||
* with an accuracy target of within 500ms. This makes it particularly useful
|
||||
* for timers with delays of 1 second or more, where exact timing is less
|
||||
* crucial.
|
||||
*
|
||||
* It's important to note that Node.js timers are inherently imprecise, as
|
||||
* delays can occur due to the event loop being blocked by other operations.
|
||||
* Consequently, timers may trigger later than their scheduled time.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The fastNow variable contains the internal fast timer clock value.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
let fastNow = 0
|
||||
|
||||
/**
|
||||
* RESOLUTION_MS represents the target resolution time in milliseconds.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 1000
|
||||
*/
|
||||
const RESOLUTION_MS = 1e3
|
||||
|
||||
/**
|
||||
* TICK_MS defines the desired interval in milliseconds between each tick.
|
||||
* The target value is set to half the resolution time, minus 1 ms, to account
|
||||
* for potential event loop overhead.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 499
|
||||
*/
|
||||
const TICK_MS = (RESOLUTION_MS >> 1) - 1
|
||||
|
||||
/**
|
||||
* fastNowTimeout is a Node.js timer used to manage and process
|
||||
* the FastTimers stored in the `fastTimers` array.
|
||||
*
|
||||
* @type {NodeJS.Timeout}
|
||||
*/
|
||||
let fastNowTimeout
|
||||
|
||||
/**
|
||||
* The kFastTimer symbol is used to identify FastTimer instances.
|
||||
*
|
||||
* @type {Symbol}
|
||||
*/
|
||||
const kFastTimer = Symbol('kFastTimer')
|
||||
|
||||
/**
|
||||
* The fastTimers array contains all active FastTimers.
|
||||
*
|
||||
* @type {FastTimer[]}
|
||||
*/
|
||||
const fastTimers = []
|
||||
|
||||
/**
|
||||
* These constants represent the various states of a FastTimer.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The `NOT_IN_LIST` constant indicates that the FastTimer is not included
|
||||
* in the `fastTimers` array. Timers with this status will not be processed
|
||||
* during the next tick by the `onTick` function.
|
||||
*
|
||||
* A FastTimer can be re-added to the `fastTimers` array by invoking the
|
||||
* `refresh` method on the FastTimer instance.
|
||||
*
|
||||
* @type {-2}
|
||||
*/
|
||||
const NOT_IN_LIST = -2
|
||||
|
||||
/**
|
||||
* The `TO_BE_CLEARED` constant indicates that the FastTimer is scheduled
|
||||
* for removal from the `fastTimers` array. A FastTimer in this state will
|
||||
* be removed in the next tick by the `onTick` function and will no longer
|
||||
* be processed.
|
||||
*
|
||||
* This status is also set when the `clear` method is called on the FastTimer instance.
|
||||
*
|
||||
* @type {-1}
|
||||
*/
|
||||
const TO_BE_CLEARED = -1
|
||||
|
||||
/**
|
||||
* The `PENDING` constant signifies that the FastTimer is awaiting processing
|
||||
* in the next tick by the `onTick` function. Timers with this status will have
|
||||
* their `_idleStart` value set and their status updated to `ACTIVE` in the next tick.
|
||||
*
|
||||
* @type {0}
|
||||
*/
|
||||
const PENDING = 0
|
||||
|
||||
/**
|
||||
* The `ACTIVE` constant indicates that the FastTimer is active and waiting
|
||||
* for its timer to expire. During the next tick, the `onTick` function will
|
||||
* check if the timer has expired, and if so, it will execute the associated callback.
|
||||
*
|
||||
* @type {1}
|
||||
*/
|
||||
const ACTIVE = 1
|
||||
|
||||
/**
|
||||
* The onTick function processes the fastTimers array.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function onTick () {
|
||||
/**
|
||||
* Increment the fastNow value by the TICK_MS value, despite the actual time
|
||||
* that has passed since the last tick. This approach ensures independence
|
||||
* from the system clock and delays caused by a blocked event loop.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
fastNow += TICK_MS
|
||||
|
||||
/**
|
||||
* The `idx` variable is used to iterate over the `fastTimers` array.
|
||||
* Expired timers are removed by replacing them with the last element in the array.
|
||||
* Consequently, `idx` is only incremented when the current element is not removed.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
let idx = 0
|
||||
|
||||
/**
|
||||
* The len variable will contain the length of the fastTimers array
|
||||
* and will be decremented when a FastTimer should be removed from the
|
||||
* fastTimers array.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
let len = fastTimers.length
|
||||
|
||||
while (idx < len) {
|
||||
/**
|
||||
* @type {FastTimer}
|
||||
*/
|
||||
const timer = fastTimers[idx]
|
||||
|
||||
// If the timer is in the ACTIVE state and the timer has expired, it will
|
||||
// be processed in the next tick.
|
||||
if (timer._state === PENDING) {
|
||||
// Set the _idleStart value to the fastNow value minus the TICK_MS value
|
||||
// to account for the time the timer was in the PENDING state.
|
||||
timer._idleStart = fastNow - TICK_MS
|
||||
timer._state = ACTIVE
|
||||
} else if (
|
||||
timer._state === ACTIVE &&
|
||||
fastNow >= timer._idleStart + timer._idleTimeout
|
||||
) {
|
||||
timer._state = TO_BE_CLEARED
|
||||
timer._idleStart = -1
|
||||
timer._onTimeout(timer._timerArg)
|
||||
}
|
||||
|
||||
if (timer._state === TO_BE_CLEARED) {
|
||||
timer._state = NOT_IN_LIST
|
||||
|
||||
// Move the last element to the current index and decrement len if it is
|
||||
// not the only element in the array.
|
||||
if (--len !== 0) {
|
||||
fastTimers[idx] = fastTimers[len]
|
||||
}
|
||||
} else {
|
||||
++idx
|
||||
}
|
||||
}
|
||||
|
||||
// Set the length of the fastTimers array to the new length and thus
|
||||
// removing the excess FastTimers elements from the array.
|
||||
fastTimers.length = len
|
||||
|
||||
// If there are still active FastTimers in the array, refresh the Timer.
|
||||
// If there are no active FastTimers, the timer will be refreshed again
|
||||
// when a new FastTimer is instantiated.
|
||||
if (fastTimers.length !== 0) {
|
||||
refreshTimeout()
|
||||
}
|
||||
}
|
||||
|
||||
function refreshTimeout () {
|
||||
// If the fastNowTimeout is already set, refresh it.
|
||||
if (fastNowTimeout) {
|
||||
fastNowTimeout.refresh()
|
||||
// fastNowTimeout is not instantiated yet, create a new Timer.
|
||||
} else {
|
||||
clearTimeout(fastNowTimeout)
|
||||
fastNowTimeout = setTimeout(onTick, TICK_MS)
|
||||
|
||||
// If the Timer has an unref method, call it to allow the process to exit if
|
||||
// there are no other active handles.
|
||||
if (fastNowTimeout.unref) {
|
||||
fastNowTimeout.unref()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `FastTimer` class is a data structure designed to store and manage
|
||||
* timer information.
|
||||
*/
|
||||
class FastTimer {
|
||||
[kFastTimer] = true
|
||||
|
||||
/**
|
||||
* The state of the timer, which can be one of the following:
|
||||
* - NOT_IN_LIST (-2)
|
||||
* - TO_BE_CLEARED (-1)
|
||||
* - PENDING (0)
|
||||
* - ACTIVE (1)
|
||||
*
|
||||
* @type {-2|-1|0|1}
|
||||
* @private
|
||||
*/
|
||||
_state = NOT_IN_LIST
|
||||
|
||||
/**
|
||||
* The number of milliseconds to wait before calling the callback.
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
_idleTimeout = -1
|
||||
|
||||
/**
|
||||
* The time in milliseconds when the timer was started. This value is used to
|
||||
* calculate when the timer should expire.
|
||||
*
|
||||
* @type {number}
|
||||
* @default -1
|
||||
* @private
|
||||
*/
|
||||
_idleStart = -1
|
||||
|
||||
/**
|
||||
* The function to be executed when the timer expires.
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
_onTimeout
|
||||
|
||||
/**
|
||||
* The argument to be passed to the callback when the timer expires.
|
||||
*
|
||||
* @type {*}
|
||||
* @private
|
||||
*/
|
||||
_timerArg
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Function} callback A function to be executed after the timer
|
||||
* expires.
|
||||
* @param {number} delay The time, in milliseconds that the timer should wait
|
||||
* before the specified function or code is executed.
|
||||
* @param {*} arg
|
||||
*/
|
||||
constructor (callback, delay, arg) {
|
||||
this._onTimeout = callback
|
||||
this._idleTimeout = delay
|
||||
this._timerArg = arg
|
||||
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timer's start time to the current time, and reschedules the timer
|
||||
* to call its callback at the previously specified duration adjusted to the
|
||||
* current time.
|
||||
* Using this on a timer that has already called its callback will reactivate
|
||||
* the timer.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
refresh () {
|
||||
// In the special case that the timer is not in the list of active timers,
|
||||
// add it back to the array to be processed in the next tick by the onTick
|
||||
// function.
|
||||
if (this._state === NOT_IN_LIST) {
|
||||
fastTimers.push(this)
|
||||
}
|
||||
|
||||
// If the timer is the only active timer, refresh the fastNowTimeout for
|
||||
// better resolution.
|
||||
if (!fastNowTimeout || fastTimers.length === 1) {
|
||||
refreshTimeout()
|
||||
}
|
||||
|
||||
// Setting the state to PENDING will cause the timer to be reset in the
|
||||
// next tick by the onTick function.
|
||||
this._state = PENDING
|
||||
}
|
||||
|
||||
/**
|
||||
* The `clear` method cancels the timer, preventing it from executing.
|
||||
*
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
clear () {
|
||||
// Set the state to TO_BE_CLEARED to mark the timer for removal in the next
|
||||
// tick by the onTick function.
|
||||
this._state = TO_BE_CLEARED
|
||||
|
||||
// Reset the _idleStart value to -1 to indicate that the timer is no longer
|
||||
// active.
|
||||
this._idleStart = -1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This module exports a setTimeout and clearTimeout function that can be
|
||||
* used as a drop-in replacement for the native functions.
|
||||
*/
|
||||
module.exports = {
|
||||
/**
|
||||
* The setTimeout() method sets a timer which executes a function once the
|
||||
* timer expires.
|
||||
* @param {Function} callback A function to be executed after the timer
|
||||
* expires.
|
||||
* @param {number} delay The time, in milliseconds that the timer should
|
||||
* wait before the specified function or code is executed.
|
||||
* @param {*} [arg] An optional argument to be passed to the callback function
|
||||
* when the timer expires.
|
||||
* @returns {NodeJS.Timeout|FastTimer}
|
||||
*/
|
||||
setTimeout (callback, delay, arg) {
|
||||
// If the delay is less than or equal to the RESOLUTION_MS value return a
|
||||
// native Node.js Timer instance.
|
||||
return delay <= RESOLUTION_MS
|
||||
? setTimeout(callback, delay, arg)
|
||||
: new FastTimer(callback, delay, arg)
|
||||
},
|
||||
/**
|
||||
* The clearTimeout method cancels an instantiated Timer previously created
|
||||
* by calling setTimeout.
|
||||
*
|
||||
* @param {NodeJS.Timeout|FastTimer} timeout
|
||||
*/
|
||||
clearTimeout (timeout) {
|
||||
// If the timeout is a FastTimer, call its own clear method.
|
||||
if (timeout[kFastTimer]) {
|
||||
/**
|
||||
* @type {FastTimer}
|
||||
*/
|
||||
timeout.clear()
|
||||
// Otherwise it is an instance of a native NodeJS.Timeout, so call the
|
||||
// Node.js native clearTimeout function.
|
||||
} else {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The setFastTimeout() method sets a fastTimer which executes a function once
|
||||
* the timer expires.
|
||||
* @param {Function} callback A function to be executed after the timer
|
||||
* expires.
|
||||
* @param {number} delay The time, in milliseconds that the timer should
|
||||
* wait before the specified function or code is executed.
|
||||
* @param {*} [arg] An optional argument to be passed to the callback function
|
||||
* when the timer expires.
|
||||
* @returns {FastTimer}
|
||||
*/
|
||||
setFastTimeout (callback, delay, arg) {
|
||||
return new FastTimer(callback, delay, arg)
|
||||
},
|
||||
/**
|
||||
* The clearTimeout method cancels an instantiated FastTimer previously
|
||||
* created by calling setFastTimeout.
|
||||
*
|
||||
* @param {FastTimer} timeout
|
||||
*/
|
||||
clearFastTimeout (timeout) {
|
||||
timeout.clear()
|
||||
},
|
||||
/**
|
||||
* The now method returns the value of the internal fast timer clock.
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
now () {
|
||||
return fastNow
|
||||
},
|
||||
/**
|
||||
* Trigger the onTick function to process the fastTimers array.
|
||||
* Exported for testing purposes only.
|
||||
* Marking as deprecated to discourage any use outside of testing.
|
||||
* @deprecated
|
||||
* @param {number} [delay=0] The delay in milliseconds to add to the now value.
|
||||
*/
|
||||
tick (delay = 0) {
|
||||
fastNow += delay - RESOLUTION_MS + 1
|
||||
onTick()
|
||||
onTick()
|
||||
},
|
||||
/**
|
||||
* Reset FastTimers.
|
||||
* Exported for testing purposes only.
|
||||
* Marking as deprecated to discourage any use outside of testing.
|
||||
* @deprecated
|
||||
*/
|
||||
reset () {
|
||||
fastNow = 0
|
||||
fastTimers.length = 0
|
||||
clearTimeout(fastNowTimeout)
|
||||
fastNowTimeout = null
|
||||
},
|
||||
/**
|
||||
* Exporting for testing purposes only.
|
||||
* Marking as deprecated to discourage any use outside of testing.
|
||||
* @deprecated
|
||||
*/
|
||||
kFastTimer
|
||||
}
|
Reference in New Issue
Block a user