andimeier
andimeier

Reputation: 1253

jQuerys jqXHR deferreds fail to chain with $.when(...)

I want to wait for multiple promises to be resolved. The method of choice is using jQuery's $.when() function to wait for all of them:

let test = $.Deferred();
$.getJSON('test.json')
    .done((content) => {
        test.resolve(content);
    });

$.when(
    test,
    $.when(17)
).done((testContent, value) => {
    console.log('test content: ' + testContent.first);
    console.log('value: ' + value);
});

The used test JSON file (test.json) looks like this:

{
  "first": "one",
  "second": "two"
}

The expected and actual output on the console is:

test content: one
value: 17

But: (Lo And Behold!)

$.when(
    $.getJSON('test.json'),
    $.when(17)
).done((testContent, value) => {
    console.log('test content: ' + testContent.first);
    console.log('value: ' + value);
});

With this code, the output changes to:

test content: undefined
value: 17

WHAT? undefined? Please note that the only difference in code is that the $.getJSON function is used directly as one of the deferreds in the $.when instruction instead of being extracted to a separate deferred.

Why? What's the difference?

It seems to me like jqXHR style deferreds are not 100 % compatible to jQuery's $.when function.

Why does this work, but I cannot use the $.getJSON jqXHR object directly in the $.when function?

I am using jQuery 3.2.1.

Update 29.04.2017

I created a fiddle.

As you can see, if I embedd the jqXHR promise directly in the $.when instruction, the result of this promise is not only the JSON value, but an array of:

[ JSONValue, statusCode, XHR ]

Why does jqXHR behave differently in $.when then when used outside of $.when?

Upvotes: 1

Views: 41

Answers (1)

Barmar
Barmar

Reputation: 780842

You're getting an array of values because jqXHR resolves to multiple values (the 3 arguments that are normally passed to the .done() method). From the $.when documentation:

If a Deferred resolved to a single value, the corresponding argument will hold that value. In the case where a Deferred resolved to multiple values, the corresponding argument will be an array of those values.

So you need to index or destructure the result to get the value you want.

$.when(
    $.getJSON('test.json'),
    $.when(17)
).done(([testContent, textStatus, jqXHR], value) => {
    console.log('test content: ' + testContent.first);
    console.log('value: ' + value);
});

You're not seeing this when you use the separate promise, because you're just resolving it with a single value in

(content) => {
    test.resolve(content);
}

You would see the same array behavior if you passed all the parameters when resolving the promise:

let test = $.Deferred();
$.getJSON('https://api.myjson.com/bins/bmguh')
  .done((content, status, jqXHR) => {
    test.resolve(content, status, jqXHR);
  });

$.when(
  test,
  $.when(17)
).done((testContent, value) => {
  console.log('Separate promise:', JSON.stringify(testContent));
});

// jqXHR used directly in $.when:
$.when(
  $.getJSON('https://api.myjson.com/bins/bmguh'),
  $.when(17)
).done((testContent, value) => {
  console.log('jqXHR used directly:', JSON.stringify(testContent));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Upvotes: 1

Related Questions