current commit
261024.4 · lib: speed up AbortSignal
// Flags: --expose-gc

import '../common/index.mjs';
import { gcUntil } from '../common/gc.js';
import assert from 'node:assert/strict';
import { it } from 'node:test';

// The default abort reason must not keep the objects on the caller's stack
// alive. It captures no stack at all. Refs: https://github.com/nodejs/node/issues/66192

class Job {
  controller = new AbortController();

  cancel() {
    this.controller.abort();
    return this.controller.signal;
  }

  cancelStatic() {
    return AbortSignal.abort();
  }
}

for (const method of ['cancel', 'cancelStatic']) {
  it(`does not retain the caller through the default reason (${method})`, async () => {
    let job = new Job();
    const jobRef = new WeakRef(job);
    const signal = job[method]();
    job = null;

    await gcUntil('job is collected', () => jobRef.deref() === undefined);
    assert.strictEqual(signal.aborted, true);
    assert.ok(signal.reason instanceof DOMException);
    assert.strictEqual(signal.reason.name, 'AbortError');
    assert.strictEqual(signal.reason.message, 'This operation was aborted');
    assert.doesNotMatch(signal.reason.stack, /\n\s+at /);
  });
}

it('creates a new default reason for every abort', () => {
  assert.notStrictEqual(new Job().cancel().reason, new Job().cancel().reason);
  assert.notStrictEqual(AbortSignal.abort().reason, AbortSignal.abort().reason);
});

it('restores Error.stackTraceLimit', () => {
  const { stackTraceLimit } = Error;
  Error.stackTraceLimit = 7;
  try {
    new Job().cancel();
    new Job().cancelStatic();
    assert.strictEqual(Error.stackTraceLimit, 7);
  } finally {
    Error.stackTraceLimit = stackTraceLimit;
  }
});

it('does not throw when Error.prepareStackTrace throws', () => {
  const { prepareStackTrace } = Error;
  Error.prepareStackTrace = () => { throw new Error('boom'); };
  try {
    assert.strictEqual(new Job().cancel().reason.name, 'AbortError');
    assert.strictEqual(new Job().cancelStatic().reason.name, 'AbortError');
  } finally {
    Error.prepareStackTrace = prepareStackTrace;
  }
});