Frontmatter
| id | 8801 |
| title | Implement generic async destruction handling via Promise Rejection |
| state | Closed |
| labels | aiarchitectureperformancecore |
| assignees | tobiu |
| createdAt | Jan 19, 2026, 10:29 AM |
| updatedAt | Jan 19, 2026, 11:17 AM |
| githubUrl | https://github.com/neomjs/neo/issues/8801 |
| author | tobiu |
| commentsCount | 1 |
| parentIssue | null |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| blockedBy | [] |
| blocking | [] |
| closedAt | Jan 19, 2026, 11:17 AM |
Implement generic async destruction handling via Promise Rejection
tobiu assigned to @tobiu on Jan 19, 2026, 10:30 AM
tobiu closed this issue on Jan 19, 2026, 11:17 AM

tobiu
Jan 19, 2026, 11:17 AM
Input from Gemini 3 Pro:
✦ I have implemented the generic async destruction handling and verified it with unit tests and by updating
MagicMoveText.Changes:
src/Neo.mjs: AddedNeo.isDestroyedsymbol and a globalunhandledrejectionhandler to suppress errors rejected with this symbol.src/core/Base.mjs:
- Refactored
#timeoutIdsarray to#timeoutsMap (id -> reject).- Updated
destroy()to reject all pending timeouts withNeo.isDestroyed.- Updated
timeout()to register and deregister reject functions.src/component/MagicMoveText.mjs:
- Removed manual
if (me.isDestroyed) return;checks afterawait me.timeout(...).- Updated the
catchblock to explicitly ignoreNeo.isDestroyedrejections to prevent unwanted retries.test/playwright/unit/core/BaseTimeout.spec.mjs: Added a comprehensive test suite to verify:
- Timeout resolution.
- Rejection on destruction.
- Concurrent timeout handling.
- Cleanup of completed timeouts.
Verification: The new test suite passed successfully, confirming that
timeout()promises are now correctly rejected when an instance is destroyed, preventing memory leaks and zombie processes. The integration withMagicMoveTextdemonstrates the cleaner, guard-free usage pattern (for timeouts).Next Steps: Other async methods like
promiseUpdateandgetDomRectcurrently do not auto-reject on destruction. This should be addressed in follow-up tasks to achieve full coverage.
Currently,
core.Base.timeoutpromises hang forever if the component is destroyed before completion, causing memory leaks (async closures never GC'd). ManualisDestroyedchecks after awaits are brittle.Plan:
Neo.DESTROYEDconstant.unhandledrejectionhandler to suppressNeo.DESTROYEDerrors.core.Base:timeoutreject functions.destroy(), reject all pending timeouts withNeo.DESTROYED.MagicMoveTextas a verification step.