October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk5 min

How to Test Partial Failures in JavaScript with Promise.allSettled()

Test JavaScript partial failures with controlled promises: assert each Promise.allSettled() result by input position, verify the aggregate waits, and separate rejected inputs from synchronous construction errors.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To test partial failures with Promise.allSettled(), make some operations fulfill and others reject, then check that the wrapper returns one outcome record per input in the original input order. A rejected operation should appear as { status: "rejected", reason }; it should not reject the aggregate promise. The examples below use controlled promises so tests do not depend on timing luck.

What the test should prove

Promise.allSettled(iterable) waits until every input settles, then fulfills with an array of outcome records. Fulfilled records contain status: "fulfilled" and a value; rejected records contain status: "rejected" and a reason. Records match the inputs’ order, not the order in which those inputs settle. MDN’s Promise.allSettled() reference describes the result, and the ECMAScript 2025 specification defines the aggregation behavior.

A useful test therefore checks both the status and the associated payload at each position. Checking only that the returned array contains one rejection is not enough: it may miss a result-order bug or a lost value.

Test a mixed success and failure

Here is a runner-agnostic example using Node’s built-in test runner and strict assertions. The wrapper is intentionally small; in a real test, call the application function whose behavior you want to verify.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import test from 'node:test';
import assert from 'node:assert/strict';

function deferred() {
  let resolve;
  let reject;
  const promise = new Promise((res, rej) => {
    resolve = res;
    reject = rej;
  });
  return { promise, resolve, reject };
}

async function loadIndependentTasks(tasks) {
  return Promise.allSettled(tasks);
}

test('reports a fulfillment and a rejection', async () => {
  const success = deferred();
  const failure = deferred();
  const error = new Error('service unavailable');

  const resultPromise = loadIndependentTasks([
    success.promise,
    failure.promise,
  ]);

  success.resolve({ id: 7 });
  failure.reject(error);

  const results = await resultPromise;

  assert.equal(results.length, 2);
  assert.deepEqual(results[0], {
    status: 'fulfilled',
    value: { id: 7 },
  });
  assert.equal(results[1].status, 'rejected');
  assert.equal(results[1].reason, error);
});

The assertion on reason uses identity because the example promises to expose the original Error object. If the application transforms errors, assert the transformed contract instead—for example, the documented error code or message. Keep those application-level expectations distinct from assertions about the built-in method.

Prove that result order follows input order

Resolve the second input before the first, then assert that the output still maps slot zero to the first input and slot one to the second. This catches code that accidentally associates results by completion order.

test('keeps results in input order when settlement order differs', async () => {
  const first = deferred();
  const second = deferred();
  const resultPromise = Promise.allSettled([
    first.promise,
    second.promise,
  ]);

  second.resolve('second input');
  first.resolve('first input');

  assert.deepEqual(await resultPromise, [
    { status: 'fulfilled', value: 'first input' },
    { status: 'fulfilled', value: 'second input' },
  ]);
});

For a more demanding mapping test, make one input reject and the other fulfill, and settle them in the reverse of their array order. Assert both the position and each record’s status and payload.

Prove that the aggregate waits for every input

Two settled inputs do not make the aggregate complete while a third remains pending. Use an explicit signal to observe completion rather than a sleep: timers can make a test slow or flaky without proving the relevant state.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
test('waits until the final input settles', async () => {
  const first = deferred();
  const second = deferred();
  const final = deferred();
  let aggregateCompleted = false;

  const resultPromise = Promise.allSettled([
    first.promise,
    second.promise,
    final.promise,
  ]).then((results) => {
    aggregateCompleted = true;
    return results;
  });

  first.resolve('done');
  second.reject(new Error('failed'));
  await Promise.resolve();

  assert.equal(aggregateCompleted, false);

  final.resolve('last');
  const results = await resultPromise;

  assert.equal(aggregateCompleted, true);
  assert.deepEqual(results, [
    { status: 'fulfilled', value: 'done' },
    { status: 'rejected', reason: assert.anything },
    { status: 'fulfilled', value: 'last' },
  ]);
});

In Node’s node:assert/strict, replace the illustrative rejected-record assertion above with an explicit identity check, since assert.anything is not a built-in matcher:

const error = new Error('failed');
// Pass error to second.reject(error), then after awaiting:
assert.equal(results[1].status, 'rejected');
assert.equal(results[1].reason, error);

Equivalently, create the error before calling reject in the full test and use it in the final expected records. That keeps the test executable without depending on matcher APIs from a different framework.

Cover the other input shapes

MDN also documents behavior that is worth checking when it matters to your wrapper: an empty iterable produces an already-fulfilled aggregate with an empty result array, and non-promise values are accepted as inputs and treated as fulfilled values. The reference includes examples of both cases.

test('handles an empty iterable', async () => {
  assert.deepEqual(await Promise.allSettled([]), []);
});

test('treats a plain value as a fulfilled input', async () => {
  assert.deepEqual(await Promise.allSettled([42]), [
    { status: 'fulfilled', value: 42 },
  ]);
});

If the application can receive an empty task list or non-promise values, test its public wrapper with those inputs too. Testing the built-in directly is useful for understanding its contract, but does not establish that your wrapper constructs or returns the right inputs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Separate rejected promises from synchronous construction errors

A rejection is an asynchronous outcome from an input promise. A synchronous throw while building the array happens before Promise.allSettled() is invoked, so it cannot become one of the method’s rejected result records. Test that path separately when input construction is part of the function’s behavior.

function collect() {
  const tasks = [makeFirstTask(), makeSecondTask()];
  return Promise.allSettled(tasks);
}

test('surfaces a synchronous error while constructing tasks', () => {
  assert.throws(() => collect(), /configuration/);
});

If the surrounding function is declared async, a throw inside it becomes a rejection of that function’s returned promise; assert that wrapper-level behavior with the runner’s rejection assertion. In either form, do not expect the construction error to appear as an individual allSettled record unless the application deliberately catches it and converts it into an input promise.

Choose the combinator that matches the failure policy

Use the caller’s requirement—not a preference for one API—as the decision rule. Promise.all() rejects when an input rejects; Promise.allSettled() waits for every input and reports every result. MDN’s Promise.all() reference explains the contrasting failure behavior.

Caller needs Use What to test
Every task must succeed for the overall operation to be useful Promise.all() Verify fulfillment when all succeed and rejection when an input rejects.
A complete report of independent outcomes, including partial failures Promise.allSettled() Verify every input has a corresponding record and that both successes and failures are represented.

Use the project’s test runner and mock at the boundary

The core test pattern—control promises, invoke the function, await the result, and assert its contract—works across test runners. For Node.js, the Node.js v26.10.0 test-runner documentation covers asynchronous tests and mocking. Its module-mocking facility has startup-flag and loader caveats; verify those requirements against the runtime and documentation for the version your project actually uses.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When the wrapper calls a network or storage dependency, mock that dependency at the boundary so the test can deterministically choose which operation succeeds or fails. Do not mock Promise.allSettled() in a test intended to verify that the wrapper aggregates outcomes correctly; doing so removes the behavior the test is supposed to exercise.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Shenzhen desk3 min
    HONOR Expands Beyond Smartphones With Humanoid Robot RevealHONOR said it unveiled its first humanoid robot at MWC 2026 and named shopping assistance, workplace inspections, and supportive companionship as intended uses. Later Robotics D1 claims and a reported…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.