Skip to content

Deferred: Preserve synchronous resolution in jQuery.when for Deferreds#5784

Open
Nicknamess96 wants to merge 1 commit into
jquery:mainfrom
Nicknamess96:fix/when-synchronous-deferred-resolution
Open

Deferred: Preserve synchronous resolution in jQuery.when for Deferreds#5784
Nicknamess96 wants to merge 1 commit into
jquery:mainfrom
Nicknamess96:fix/when-synchronous-deferred-resolution

Conversation

@Nicknamess96
Copy link
Copy Markdown

When jQuery.when() is called with a single pending jQuery Deferred, the returned
promise now resolves synchronously (via .done()/.fail()) instead of being
scheduled asynchronously via setTimeout through .then().

The Problem

As reported in #4798, jQuery.when(deferred) unnecessarily introduces asynchronous
behavior when the single argument is a jQuery Deferred:

const deferred = jQuery.Deferred();
const promise = jQuery.when(deferred);
promise.done(() => console.log("done"));
console.log("before");
deferred.resolve();
console.log("after");
// Before fix: "before", "after", "done" (async via setTimeout)
// After fix:  "before", "done", "after" (synchronous)

This happens because jQuery.when returns primary.then() for pending single-value
cases, and .then() schedules handlers via window.setTimeout per the Promises/A+
spec. However, jQuery Deferreds use .done()/.fail() for synchronous notification,
so this async scheduling is unnecessary when the argument is a Deferred.

The Fix

When the single argument has a .promise() method (i.e., is a jQuery Deferred),
pipe resolution through a subordinate Deferred using .done()/.fail() instead
of .then(). The resolved value is re-adopted via adoptValue to preserve
secondary thenable unwrapping (cf. gh-3000).

Non-jQuery thenables (native Promises, custom thenables) still use the .then()
path, preserving their expected async behavior.

Tests

Added a new test case "jQuery.when(pendingDeferred) - synchronous resolution (gh-4798)" that verifies:

  • .done() handler fires synchronously on resolve
  • .fail() handler fires synchronously on reject
  • Resolved values are correctly propagated
  • Multi-value resolution preserves all values
  • Promise state updates synchronously

All existing tests pass, including the Promises/A+ compliance suite (872/872).

Fixes gh-4798

When jQuery.when() is called with a single pending jQuery Deferred,
pipe through a subordinate Deferred using .done()/.fail() instead of
.then() to avoid unnecessary asynchronous scheduling via setTimeout.

The existing .then() path is still used for non-jQuery thenables
(e.g., native Promises) which require async unwrapping. Secondary
thenable unwrapping is preserved by re-adopting the resolved value
through adoptValue on the subordinate.

Fixes jquerygh-4798
Closes jquerygh-4798
@linux-foundation-easycla
Copy link
Copy Markdown

linux-foundation-easycla Bot commented Feb 25, 2026

CLA Signed

The committers listed above are authorized under a signed CLA.

  • ✅ login: Nicknamess96 / name: Nicknamess96 (cacd637)

@Nicknamess96
Copy link
Copy Markdown
Author

Friendly ping — this fixes a regression where jQuery.when doesn't preserve synchronous resolution for Deferreds. Tests pass. Happy to address any feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

jQuery.when: unnecessary asynchronous behavior when called with only one argument

1 participant