08edfcde2350ca16ecb21cbb2a267e67c2fdaa36
3 const { ChoreError
} = require('./errors');
4 const { fileScope
} = require('@squeep/log-helper');
5 const _fileScope
= fileScope(__filename
);
8 * @typedef {object} ConsoleLike
9 * @property {Function} debug debug
10 * @property {Function} error error
13 * @typedef {object} Chore
14 * @property {boolean} isRunning actively being executed
15 * @property {Function} choreFn task handler to invoke
16 * @property {number} intervalMs period to wait between invocations
17 * @property {NodeJS.Timeout=} timeoutObj active timeout
18 * @property {Date=} nextSchedule time of next invocation
22 * Thin wrapper for wrangling periodic tasks with setTimeout.
26 * @param {ConsoleLike} logger logger object
35 * @param {string} choreName chore name
36 * @returns {Chore} chore
38 _getChore(choreName
) {
39 const chore
= this.chores
[choreName
]; // eslint-disable-line security/detect-object-injection
41 throw new ChoreError('chore does not exist');
47 * Register a chore task to be called every intervalMs.
48 * Zero interval will only register task, will not schedule any calls.
49 * @param {string} choreName chore name
50 * @param {() => Promise<void>} choreFn chore function
51 * @param {number} intervalMs invocation period
53 establishChore(choreName
, choreFn
, intervalMs
= 0) {
54 const _scope
= _fileScope('establishChore');
56 if (choreName
in this.chores
) {
57 this.logger
.error(_scope
, 'chore already exists', { choreName
});
58 throw new ChoreError('chore exists');
61 const managedChoreFn
= async () => this.runChore(choreName
);
62 this.chores
[choreName
] = { // eslint-disable-line security/detect-object-injection
66 timeoutObj: intervalMs
? setTimeout(managedChoreFn
, intervalMs
).unref() : undefined,
67 nextSchedule: intervalMs
? new Date(Date
.now() + intervalMs
) : undefined,
72 * Invoke a chore task off-schedule, with any arguments.
73 * Resets timer of any next scheduled call.
74 * @param {string} choreName chore name
75 * @param {...any} choreArgs additional args to pass to chore function
76 * @returns {Promise<void>} nothing
78 async
runChore(choreName
, ...choreArgs
) {
79 const _scope
= _fileScope('runChore');
81 const chore
= this._getChore(choreName
);
83 if (chore
.isRunning
) {
84 this.logger
.debug(_scope
, 'chore already running, skipping', { choreName
});
88 chore
.isRunning
= true;
90 await chore
.choreFn(...choreArgs
);
92 this.logger
.error(_scope
, 'chore failed', { choreName
, error: e
});
93 if (choreArgs
.length
) {
94 // If args were supplied, was invoked off-schedule, propagate the error.
98 chore
.isRunning
= false;
99 if (chore
.intervalMs
) {
100 chore
.timeoutObj
.refresh();
101 chore
.nextSchedule
= new Date(Date
.now() + chore
.intervalMs
);
107 * Stop a chore from being scheduled to run.
108 * @param {string} choreName chore name
110 stopChore(choreName
) {
111 const _scope
= _fileScope('stopChore');
113 const chore
= this._getChore(choreName
);
114 clearTimeout(chore
.timeoutObj
);
115 chore
.timeoutObj
= undefined;
116 chore
.nextSchedule
= undefined;
117 this.logger
.debug(_scope
, 'stopped chore', { chore
});
121 for (const choreName
in this.chores
) {
122 this.stopChore(choreName
);
128 module
.exports
= Chores
;