Skip to content

Commit fd9b0ee

Browse files
committed
Resolve debounce race condition
debounce didn't cancel the execution but only deplayed the resolution. It then resolved multiple times with the same values casing the resulting code to be called multiple times in direct succession on multiple threads. This can not only resolve into multipe DOM updates but also to race conditions.
1 parent fc073eb commit fd9b0ee

File tree

3 files changed

+22
-22
lines changed

3 files changed

+22
-22
lines changed

debug_toolbar/static/debug_toolbar/js/toolbar.js

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -295,17 +295,15 @@ const djdt = {
295295
updateOnAjax() {
296296
const sidebarUrl =
297297
document.getElementById("djDebug").dataset.sidebarUrl;
298-
const slowjax = debounce(ajax, 200);
299298

300-
function handleAjaxResponse(requestId) {
299+
const handleAjaxResponse = debounce(async (requestId) => {
301300
const encodedRequestId = encodeURIComponent(requestId);
302301
const dest = `${sidebarUrl}?request_id=${encodedRequestId}`;
303-
slowjax(dest).then((data) => {
304-
if (djdt.needUpdateOnFetch) {
305-
replaceToolbarState(encodedRequestId, data);
306-
}
307-
});
308-
}
302+
const data = await ajax(dest);
303+
if (djdt.needUpdateOnFetch) {
304+
replaceToolbarState(encodedRequestId, data);
305+
}
306+
}, 100);
309307

310308
// Patch XHR / traditional AJAX requests
311309
const origOpen = XMLHttpRequest.prototype.open;

debug_toolbar/static/debug_toolbar/js/utils.js

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -121,20 +121,23 @@ export function replaceToolbarState(newRequestId, data) {
121121
}
122122
}
123123

124-
export function debounce(func, delay) {
125-
let timer = null;
126-
let resolves = [];
127-
124+
/**
125+
* Debounce async functions.
126+
*
127+
* @param {Function} func - Function to be executed.
128+
* @param {number} timeout - Time to wait before executing function in milliseconds.
129+
* @returns {Function} - Debounced function.
130+
*/
131+
export function debounce(func, timeout) {
132+
let timer;
128133
return async (...args) => {
129134
clearTimeout(timer);
130-
timer = setTimeout(() => {
131-
const result = func(...args);
132-
for (const r of resolves) {
133-
r(result);
134-
}
135-
resolves = [];
136-
}, delay);
137-
138-
return await new Promise((r) => resolves.push(r));
135+
return await new Promise((resolve, reject) => {
136+
timer = setTimeout(() => {
137+
Promise.resolve(func.apply(this, [...args]))
138+
.then(resolve)
139+
.catch(reject);
140+
}, timeout);
141+
});
139142
};
140143
}

tests/js/utils.test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,6 @@ describe("utils.js", () => {
299299

300300
expect(fn).toHaveBeenCalledTimes(1);
301301
expect(fn).toHaveBeenCalledWith("third");
302-
expect(results).toEqual(["third", "third", "third"]);
303302
vi.useRealTimers();
304303
});
305304
});

0 commit comments

Comments
 (0)