Skip to content

Commit e023225

Browse files
chikara1608claude
andcommitted
feat(rule): gate sync rule-result resolution behind runTypeASync flag
Resolving each rule's result via setTimeout(0) (Deque #1172) creates one macrotask transition per rule (~335). On large DOMs with advance ON these interleave with DevTools IPC (resource.getContent), ballooning the per-rule resolve from ~8ms to ~180ms and inflating axe.run from ~14s to ~85s. When axe._cache 'runTypeASync' is set (by a11y-engine-core run.js from the a11yCoreConfig flag), resolve synchronously to eliminate the contention. The flag defaults OFF, preserving the original deferred behaviour. On the OFF path, instrument the schedule-to-fire delay of each yield and accumulate {totalMs,count,maxMs} onto axe._typeASyncYield so every scan reports how much wall-clock the async resolve costs — the exact time the flag would recover. Read back into Type A telemetry by a11y-engine-core. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0371a36 commit e023225

1 file changed

Lines changed: 32 additions & 4 deletions

File tree

lib/core/base/rule.js

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,11 +310,39 @@ Rule.prototype.run = function run(context, options = {}, resolve, reject) {
310310
if (options.performanceTimer) {
311311
this._logRulePerformance();
312312
}
313-
// Defer the rule's execution to prevent "unresponsive script" warnings.
314-
// See https://github.com/dequelabs/axe-core/pull/1172 for discussion and details.
315-
setTimeout(() => {
313+
// [a11y-core]: runTypeASync ON resolves synchronously; OFF keeps the
314+
// deferred setTimeout(0) resolve (Deque #1172) and records yield delay.
315+
if (cache.get('runTypeASync')) {
316316
resolve(ruleResult);
317-
}, 0);
317+
} else {
318+
let scheduledAt;
319+
try {
320+
scheduledAt = window.performance.now();
321+
} catch {
322+
scheduledAt = undefined;
323+
}
324+
setTimeout(() => {
325+
try {
326+
if (scheduledAt !== undefined) {
327+
const yieldDelay = window.performance.now() - scheduledAt;
328+
const yieldStats = axe._typeASyncYield || {
329+
totalMs: 0,
330+
count: 0,
331+
maxMs: 0
332+
};
333+
yieldStats.totalMs += yieldDelay;
334+
yieldStats.count += 1;
335+
if (yieldDelay > yieldStats.maxMs) {
336+
yieldStats.maxMs = yieldDelay;
337+
}
338+
axe._typeASyncYield = yieldStats;
339+
}
340+
} catch {
341+
// ignore instrumentation failures
342+
}
343+
resolve(ruleResult);
344+
}, 0);
345+
}
318346
}).catch(error => {
319347
if (options.performanceTimer) {
320348
this._logRulePerformance();

0 commit comments

Comments
 (0)