Add pause mechanism for feed updating

```
var handler = await Zotero.Feeds.pause();
[...]
handler.resume();
```
This commit is contained in:
Dan Stillman 2025-12-29 15:50:52 -05:00
parent ef2f7049c7
commit 4d7a099c32

View file

@ -28,7 +28,11 @@
// Mimics Zotero.Libraries // Mimics Zotero.Libraries
Zotero.Feeds = new function () { Zotero.Feeds = new function () {
var _initPromise; var _initPromise;
var _nextFeedCheck;
var _updating; var _updating;
var _updatePromise;
var _pauseTokens = new Set();
var _paused = false;
this.init = function () { this.init = function () {
// Delay initialization for tests // Delay initialization for tests
@ -67,6 +71,61 @@ Zotero.Feeds = new function () {
); );
}; };
this.pause = async function () {
// Wait for any current updates to stop
if (_updatePromise) {
await _updatePromise;
}
let token = Symbol();
_pauseTokens.add(token);
_applyPausedState();
let resumed = false;
return {
resume() {
if (resumed) {
return;
}
resumed = true;
_pauseTokens.delete(token);
_applyPausedState();
}
}
};
function _applyPausedState() {
let shouldPause = _pauseTokens.size > 0;
if (shouldPause == _paused) {
return;
}
_paused = shouldPause;
if (_paused) {
_pauseInternal();
}
else {
_resumeInternal();
}
}
function _isPaused() {
return _pauseTokens.size > 0;
}
function _pauseInternal() {
Zotero.debug("Pausing feed updating");
if (_nextFeedCheck) {
clearTimeout(_nextFeedCheck);
_nextFeedCheck = null;
}
}
function _resumeInternal() {
Zotero.debug("Resuming feed updating");
Zotero.Feeds.scheduleNextFeedCheck();
};
this.uninit = function () { this.uninit = function () {
// TODO: fx140: We used to explicitly cancel the Bluebird _initPromise here // TODO: fx140: We used to explicitly cancel the Bluebird _initPromise here
// Is it OK to let it complete? // Is it OK to let it complete?
@ -262,8 +321,9 @@ Zotero.Feeds = new function () {
this._nextFeedCheckDelay = null; // For tests this._nextFeedCheckDelay = null; // For tests
this.scheduleNextFeedCheck = async function () { this.scheduleNextFeedCheck = async function () {
// Don't schedule if already updating, since another check is scheduled at the end // Don't schedule if already updating, since another check is scheduled at the end, or if
if (_updating) { // paused
if (_updating || _paused) {
return; return;
} }
@ -276,19 +336,19 @@ Zotero.Feeds = new function () {
+ "ORDER BY nextCheck ASC LIMIT 1"; + "ORDER BY nextCheck ASC LIMIT 1";
var nextCheck = await Zotero.DB.valueQueryAsync(sql); var nextCheck = await Zotero.DB.valueQueryAsync(sql);
if (this._nextFeedCheck) { if (_nextFeedCheck) {
clearTimeout(this._nextFeedCheck); clearTimeout(_nextFeedCheck);
this._nextFeedCheck = null; _nextFeedCheck = null;
} }
if (nextCheck !== false) { if (nextCheck !== false) {
nextCheck = nextCheck > 0 ? nextCheck * 1000 : 0; nextCheck = nextCheck > 0 ? nextCheck * 1000 : 0;
this._nextFeedCheckDelay = nextCheck; this._nextFeedCheckDelay = nextCheck;
Zotero.debug("Next feed check in " + (nextCheck / 1000) + " seconds"); Zotero.debug("Next feed check in " + (nextCheck / 1000) + " seconds");
this._nextFeedCheck = setTimeout(async () => { _nextFeedCheck = setTimeout(async () => {
await globalFeedCheckDelay; await globalFeedCheckDelay;
this._nextFeedCheck = null; _nextFeedCheck = null;
globalFeedCheckDelay = Zotero.Promise.delay(60000); // Don't perform auto-updates more than once per minute globalFeedCheckDelay = Zotero.Promise.delay(60000); // Don't perform auto-updates more than once per minute
await this.updateFeeds(); await this.updateFeeds();
}, nextCheck); }, nextCheck);
@ -303,29 +363,42 @@ Zotero.Feeds = new function () {
Zotero.debug("Feed update already in progress"); Zotero.debug("Feed update already in progress");
return; return;
} }
if (this._nextFeedCheck) { if (_paused) {
clearTimeout(this._nextFeedCheck); Zotero.debug("Feed updating is paused");
this._nextFeedCheck = null; return;
}
if (_nextFeedCheck) {
clearTimeout(_nextFeedCheck);
_nextFeedCheck = null;
} }
_updating = true; _updating = true;
try { _updatePromise = new Promise(async (resolve) => {
let sql = "SELECT libraryID AS id FROM feeds " try {
+ "WHERE refreshInterval IS NOT NULL " let sql = "SELECT libraryID AS id FROM feeds "
+ "AND ( lastCheck IS NULL " + "WHERE refreshInterval IS NOT NULL "
+ "OR (julianday(lastCheck, 'utc') + (refreshInterval/1440.0) - julianday('now', 'utc')) <= 0 )"; + "AND ( lastCheck IS NULL "
let needUpdate = ((await Zotero.DB.queryAsync(sql))).map(row => row.id); + "OR (julianday(lastCheck, 'utc') + (refreshInterval/1440.0) - julianday('now', 'utc')) <= 0 )";
Zotero.debug("Running update for feeds: " + needUpdate.join(', ')); let needUpdate = ((await Zotero.DB.queryAsync(sql))).map(row => row.id);
for (let i=0; i<needUpdate.length; i++) { Zotero.debug("Running update for feeds: " + needUpdate.join(', '));
let feed = Zotero.Feeds.get(needUpdate[i]); for (let i=0; i<needUpdate.length; i++) {
await feed.waitForDataLoad('item'); if (_paused) {
await feed._updateFeed(); Zotero.debug("Stopping feed updates due to pause");
break;
}
let feed = Zotero.Feeds.get(needUpdate[i]);
await feed.waitForDataLoad('item');
await feed._updateFeed();
}
} }
} finally {
finally { _updating = false;
_updating = false; resolve();
} _updatePromise = null;
Zotero.debug("All feed updates done"); }
this.scheduleNextFeedCheck();
Zotero.debug("All feed updates done");
this.scheduleNextFeedCheck();
});
}; };
// Conversion from expansive to compact format sync json // Conversion from expansive to compact format sync json