0

Using jQuery 3.0.0, given

$(function() {

  var n = 5;

  function jQueryWhenApplyResolveRejectWith(n) {
    
    var arr = $.map(Array(5), function(_, i) {
      return $.Deferred();
    });

    var obj = {
      "index": null
    };

    var promises = $.when.apply(null, arr.map(function(promise, i) {
      return i < n 
             ? promise.resolveWith(obj, [i]) 
             : promise.rejectWith((obj.index = i, obj)
               , [new Error(i + " is not less than " + n)])
    }));
    
    function success(...result) {
      console.log("resolved, result:", result, "this:", this);
    }
    
    function err(error) {
      console.log("rejected, error:", error, "this:", this);
    }
    
    return promises.then(success, err);
    
  }
  
  jQueryWhenApplyResolveRejectWith(n)
  .then($.proxy(jQueryWhenApplyResolveRejectWith, null, --n))

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js">
</script>

the first call to jQueryWhenApplyResolveRejectWith should return an array of resolved jQuery promise values at .then() chained to promises, where this is an array of obj objects.

The second call to jQueryWhenApplyResolveRejectWith should return Error, with this set to single object obj.

The expected result of success is this set to single obj, as single object was passed to deferred.resolveWith.

Though the expected result is not returned, at javascript at stacksnippets, the single object can be returned by using .bind() or $.proxy() at .then() chained to promises.

$(function() {

  var n = 5;

  function jQueryWhenApplyResolveRejectWith(n) {
    
    var arr = $.map(Array(5), function(_, i) {
      return $.Deferred();
    });

    var obj = {
      "index": null
    };

    var promises = $.when.apply(null, arr.map(function(promise, i) {
      return i < n 
             ? promise.resolveWith(obj, [i]) 
             : promise.rejectWith((obj.index = i, obj)
               , [new Error(i + " is not less than " + n)])
    }));
    
    function success(...result) {
      console.log("resolved, result:", result, "this:", this);
    }
    
    function err(error) {
      console.log("rejected, error:", error, "this:", this);
    }
    
    return promises.then($.proxy(success, obj), err);
    
  }
  
  jQueryWhenApplyResolveRejectWith(n)
  .then($.proxy(jQueryWhenApplyResolveRejectWith, null, --n))

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js">
</script>

Questions:

  1. Why is this converted to an array from the plain object passed to .resolveWith(); while the same object passed to .rejectWith() returns a single object using $.when.apply() pattern?

  2. Is the expected behaviour of using $.when.apply() or .resolveWith(), or both in same procedure, this being set to an array containing original this multiplied by the number of resolved jQuery promise objects?

guest271314
  • 1
  • 15
  • 104
  • 177
  • 1
    As far as I'm aware, the jQuery documentation doesn't state the effect on `this` when multiple promises are aggregated, having been given a context with `.resolveWith()`. Therefore, expectation is somewhat personal and hard to define. Personally, I can accept the behaviour exhibited. However, I do find the behaviour somewhat quirky when promises are aggregated, having been resolved with a mixture of `resolve()` and `resolveWith()` - `this` seems OK but the result delivered via `...result` certainly doesn't meet my expectation. – Roamer-1888 Sep 18 '16 at 00:13
  • @Roamer-1888 _"but the result delivered via `...result` certainly doesn't meet my expectation"_ What is your expectation? There may have been a bug concerning this, or similar result with `.notifyWith()` at jQuery's github issues page. Will check. Did not expect `this` to be converted into an array, here. – guest271314 Sep 18 '16 at 00:17
  • @Roamer-1888 The closest related issue could find so far https://github.com/jquery/jquery/issues/1839 – guest271314 Sep 18 '16 at 00:24
  • Sorry, I take that back, I was doing something stupid. Mixed `resolve()` and `resolveWith()` behaves OK. – Roamer-1888 Sep 18 '16 at 00:41
  • @Roamer-1888 _" Sorry, I take that back"_ Take what back? _"resolveWith() behaves OK"_ Expected result is for `this` to be converted to an array having copies of original `this` multiplied by the amount of resolve jQuery promises? – guest271314 Sep 18 '16 at 00:48
  • @Roamer-1888 Do you mean that each returned has its own `this` context, and that an error handler has only a single context? – guest271314 Sep 18 '16 at 01:01
  • 1
    I find the behaviour of `jQuery.when().then()` [easier to understand after testing like this](https://jsfiddle.net/jkaes5fy/) – Roamer-1888 Sep 18 '16 at 08:46
  • @Roamer-1888 Used `{index:null}` to indicate which promise was rejected – guest271314 Sep 18 '16 at 15:01

1 Answers1

0

Why is this converted to an array using .apply() with jQuery.when and jQuery.Deferred.resolveWith?

  1. Why is this converted to an array from the plain object passed to .resolveWith(); while the same object passed to .rejectWith() returns a single object using $.when.apply() pattern?
  2. Is the expected behaviour of using $.when.apply() or .resolveWith(), or both in same procedure, this being set to an array containing original this multiplied by the number of resolved jQuery promise objects?

Each promise object has its own context. The array returned as this at success function represents the this value of each resolved promise. Since the context could be changed between the promise being resolved or rejected, the this is not flattened to a single object, or single value within the returned array, but distinct for each jQuery promise object originally passed to $.when.apply().

Yes, believe this to be expected behaviour. Note that $.when.apply() is not a jQuery method, but a way to pass an array of promises to $.when() and retrieve array of resolved promises at .then(), or single rejected promise if any one of the promises in passed array is rejected.

If expected result is single this at success where an array of resolved promises is return value, you can use .bind() or $.proxy() to set this at success function.

  return i < n 
         ? promise.resolve(i) // use `.resolve()` instead of `.resolveWith()` here
         : promise.rejectWith((obj.index = i, obj)
           , [new Error(i + " is not less than " + n)])

  return promises.then(success.bind(obj), err); // use `.bind()` to set `this`

There is no need to adjust err function as only single rejected promise is returned to onRejected handler, where this would be single value, not an array.

guest271314
  • 1
  • 15
  • 104
  • 177