Corrige un deadlock dans onFilterChanged() qui gelait le tableau
ensureScrollable() (appelé par onFilterChanged() après chaque changement de filtre) appelle lui-même loadOlder()/loadNewer(), qui passent par le même _queueWindowOp -- en le chaînant *à l'intérieur* de l'opération déjà mise en file par onFilterChanged(), la file d'attente se retrouvait à attendre sa propre continuation dès qu'un filtre laissait trop peu de lignes pour remplir l'écran, gelant purement et simplement le tableau (recherche qui ne charge plus les lignes précédentes en scrollant, et même effacer le filtre ensuite ne faisait plus rien -- tout attendait derrière l'opération bloquée). Corrigé en chaînant ensureScrollable() après la résolution de l'opération mise en file, pas dedans -- ses propres appels à loadOlder()/loadNewer() s'empilent alors normalement sur la file, sans dépendance circulaire. Reproduit et vérifié en conditions réelles : recherche "Assurance local" (47 correspondances de 2023 à 2026) qui chargeait bien 2026 mais bloquait en scrollant vers le haut -- après correctif, chaque scroll vers le haut déclenche bien un nouveau chargement (vérifié sur 2 scrolls successifs), et effacer le filtre recharge immédiatement la vue complète.
This commit is contained in:
@@ -1455,14 +1455,24 @@
|
|||||||
// Every toolbar filter change routes through here: with filtering
|
// Every toolbar filter change routes through here: with filtering
|
||||||
// now server-side (see fetchFilteredLignes()), there's no more
|
// now server-side (see fetchFilteredLignes()), there's no more
|
||||||
// "just recompute a client-side view" -- the currently loaded
|
// "just recompute a client-side view" -- the currently loaded
|
||||||
// window has to be re-fetched with the new filter applied. Queued
|
// window has to be re-fetched with the new filter applied. The
|
||||||
// through the same chain as loadOlder()/loadNewer() (see
|
// reload itself is queued through the same chain as loadOlder()/
|
||||||
// _queueWindowOp) since several filters can change in the same
|
// loadNewer() (see _queueWindowOp) since several filters can change
|
||||||
// tick (e.g. mounted() restoring them all from the URL hash at
|
// in the same tick (e.g. mounted() restoring them all from the URL
|
||||||
// once), and interleaving their fetches would race on `rows` the
|
// hash at once), and interleaving their fetches would race on
|
||||||
// same way parallel loadOlder()/loadNewer() calls used to.
|
// `rows` the same way parallel loadOlder()/loadNewer() calls used
|
||||||
|
// to. ensureScrollable() is deliberately chained AFTER that queued
|
||||||
|
// op settles, not passed into it: ensureScrollable() itself calls
|
||||||
|
// loadOlder()/loadNewer(), which each enqueue their own op onto the
|
||||||
|
// very same chain -- queuing it *inside* the op currently occupying
|
||||||
|
// that chain made the chain await its own continuation (the queued
|
||||||
|
// op can't finish until its child call, appended behind it on the
|
||||||
|
// same chain, finishes first) and deadlocked solid the moment a
|
||||||
|
// filter actually left too few rows to fill the viewport, wedging
|
||||||
|
// every future filter change and scroll-triggered load right along
|
||||||
|
// with it.
|
||||||
onFilterChanged() {
|
onFilterChanged() {
|
||||||
this._queueWindowOp(() => this.reloadWindow().then(() => this.ensureScrollable()));
|
this._queueWindowOp(() => this.reloadWindow()).then(() => this.ensureScrollable());
|
||||||
this.syncHash();
|
this.syncHash();
|
||||||
},
|
},
|
||||||
// Keeps extending the window (both directions) as long as a filter
|
// Keeps extending the window (both directions) as long as a filter
|
||||||
|
|||||||
Reference in New Issue
Block a user