4

This code inside async function, does not give the expected result:

var result = await $.when( $.get('/api/1'), $.get('/api/2') );

with one request, result will be the output I expect (the response text). However, with these two requests, the returned result is an array which does not hold the two Promises values. Is there any workaround?
I know there are then() and done(), but I prefer using await.

user3599803
  • 6,435
  • 17
  • 69
  • 130
  • are you sure you don't get any error in the console? jQuery **when** returns a jQuery deferred object (not a JavaScript promise).... – gaetanoM Feb 25 '18 at 22:07
  • no warning. I get an array of 3 items. But only one responseText. – user3599803 Feb 25 '18 at 22:12
  • You get two arrays of 3 items, and the first item in each of those is the response text. – Tomalak Feb 25 '18 at 22:15
  • Hmm I get just one array. result[0] = response string, result[1] = "success", result[2] = jqXHR – user3599803 Feb 25 '18 at 22:24
  • Ah, yes. Well. Use `.done()`. `$.when()` and `await` are incompatible. Or use `await Promise.all( [$.get('/api/1'), $.get('/api/2')] )` instead. – Tomalak Feb 25 '18 at 22:28
  • Neither jQuery `$.get()` calls or `$.when()` are entirely compatible with `await` because they don't resolve with a single value. Instead, they resolve with multiple values sent as separate arguments which isn't what `await` is expecting or what the ES6 Promise specification requires. – jfriend00 Feb 25 '18 at 22:46
  • For me $.get is actually compatible with await, as it return the expected result. – user3599803 Feb 25 '18 at 22:56
  • @user3599803 - `$.get()` is sort of compatible. It calls it's completion handler with three arguments (against the Promise standard which requires only a single argument). It just so happens that `await` ignores the other two arguments and only uses the first one. If that's the only one you need (which sometimes it is), then it can be used that way. Overall, jQuery is a bit of a mess when it comes to Promise standards. Much better to use libraries that are fully compatible with the ES6 standard. – jfriend00 Feb 25 '18 at 23:03

2 Answers2

6

jQuery's .when() and the native await have different semantics. Compare:

// jQuery

$.when(promise1, promise2).done(function (result1, result2) {
    // work with result1 and result2
});

and

// native

Promise.all([promise1, promise2]).then(function (results) {
    // work with results[0] and results[1]
});

// await is just a variation of the above:

var results = await Promise.all([promise1, promise2]);
// work with results[0] and results[1]

The native implementation uses a single array of multiple promises, while jQuery's implementation expects multiple individual promises.

This means that you can't use await with $.when(). await effectively gives you the value of the first argument to the callback when the asynchronous function completes.

Using await for Promise.all() works, because the first argument will be an array of all results. Using await for $.when() won't work, because the second result will be the second argument to the callback, and so on, which means you would lose all results except the first one.

jQuery's implementation predates native promises, they designed it this way and now they have to stick with it. Such is life.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • `await $.when(…).then((...results) => results)` could work, but I agree, jQuery promises are weird and should be avoided. – Bergi Feb 25 '18 at 23:07
  • 1
    But that won't work in IE11. ;) Oh well. Anyway, there isn't a lot of reason to bend `$.when()` against its implementation if there's `Promise.all()` available, which would be the case in any environment that supports destructuring. – Tomalak Feb 25 '18 at 23:10
  • 1
    …or even supports `async`/`await` :-D – Bergi Feb 25 '18 at 23:20
1

You cannot use await, but actually I find jQuery's way of doing it quite intuitive, even the way they pass values

var d1 = $.Deferred();
var d2 = $.Deferred();
var d3 = $.Deferred();

$.when( d1, d2, d3 ).done(function ( v1, v2, v3 ) {
  // v1, v2 and v3 output the resolve
  // of d1, d2 and d3 respectively
  console.log( v1, v2, v3 ); 
});

// done method of $.when will just run when
// all deferred functions are resolved, 
// that is, after two seconds
setTimeout(()=>{d1.resolve()},1000);
setTimeout(()=>{d2.resolve("abc")},2000);
d3.resolve( 1, 2, 3, 4, 5 );

console.log('Begin...')
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
João Pimentel Ferreira
  • 14,289
  • 10
  • 80
  • 109