3

Yet another question about promises. I have this scenario:

Service.prototype.parse = function (data) {
    var deferred = $.Deferred();
    var arr      = [];
    for (var i = 0; i < data.length; i++) {
        var details = new Details();
        $.when(details).then(function (data) {
            arr.push(data);
            deferred.resolve(arr);
        });
    }

    return deferred.promise;
};

Somewhere else in the code:

...
$.when(parse()).then(function (resp) {
   //...
});

The promises get resolved at some point but initially resp has a length of 1.

How to wait for parse() to resolve everything and return a array?

Patrioticcow
  • 26,422
  • 75
  • 217
  • 337
  • @Bergi it's not a duplicate, it's just similar, maybe linking to it is appropriate but I think the questions are distant enough to be separate. This question is valuably searchable on its own. – Benjamin Gruenbaum Sep 15 '15 at 14:53
  • @BenjaminGruenbaum: Yeah, but we have so many jQuery questions concerned about multiple concurrent deferreds, it surely is a dupe :-) I just didn't decide on a good canonical yet. – Bergi Sep 15 '15 at 14:55
  • @Bergi me too and I've been trying. The thing is these questions are distinct enough that nothing is an exact duplicate, and I'm afraid showing people similar questions that are a similar (but not exact) same problem might discourage them. I think linking from within the questions is enough. – Benjamin Gruenbaum Sep 15 '15 at 14:56

2 Answers2

5

No need for a deferred anti pattern (explicit construction) or for explicit array construction. Your code can be simplified to:

Service.prototype.parse = function (data) {
     return $.when.apply($, data.map(function(x){
         return new Details(); 
     }).then(function(){ return arguments; });//.then(Array.of); // instead, if want array
};

Some general advice:

  • You're ignoring the item you're iterating in the data, I assume you don't do that in your real code but just making sure.
  • It's generally not the best idea to perform async IO in a constructor. Please consider separating construction and initialization.
  • You should consider using native promises (or a library) or using jQuery 3.0.0 which supports non problematic promises.
Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • I think there are couple issues in your suggestion: 1) `Array.map` is not supported under IE 9; 2) `parse()` function is supposed to return a promise but you didn't; 3) `arguments` is not an array. It is similar to an Array, but does not have any Array properties except length. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/arguments – JM Yang Sep 15 '15 at 14:12
  • 1
    @JMYang please read http://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it , Array.map can be polyfilled (or jQuery.map can be used) but I don't assume ancient browsers that a few people support have to be supported in my answer (unless OP states so). – Benjamin Gruenbaum Sep 15 '15 at 14:22
  • @JMYang lol, Array.map is what you point out? Array.from is not even supported in Edge. – Florian Margaine Sep 15 '15 at 14:23
  • @Florian Margaine please check https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/push, Array.push is supported from IE 5.5. Without know the environment requirement of OP, I think better compatibility is better. We don't have to use Array.map here right? In addition, this is not the main issue. The main issue is #2. For #3, if the OP wants to use any Array method on returned data he will failed, if the return data is arguments rather than array. – JM Yang Sep 15 '15 at 14:27
  • @BenjaminGruenbaum not sure if you down voted me because of I down voted you. I down voted when you didn't modify your implementation to return anything. That's definitely not a correct answer. Now you've fixed it after I pointed out. Could you please explain why you down voted my answer, what's wrong in it? Did you earn your reputation in this way? – JM Yang Sep 15 '15 at 14:35
  • 1
    @JMYang http://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it, basically if one of the promises errors in your array - your answer silently ignores the failure without giving the user any way to deal with it. If you fix your answer I will gladly undo it. I also do not appreciate allegations, while your downvote _did_ prompt me to read your answer, it's not what prompted the downvote. – Benjamin Gruenbaum Sep 15 '15 at 14:40
  • @BenjaminGruenbaum please note according to OP's question, knowing failure is not a requirement, but the return type is array is. You might be an expert of promise but not an expert of understanding the requirement. I will be glad to learn from your post, it does teach me something I don't know before. You also teach me what reputation score stands in this site as well, thank you. It can be earned after fixing you answer when someone else pointed out the issues, while down voting the person that pointed it out. And you can still earn extra reputation because of people honour you. – JM Yang Sep 15 '15 at 14:44
  • @JMYang reputation in this site is mostly _meaningless_ and only checks _participation_, votes are based on _usefulness_ and not expertise or correctness. This site is about _helping other developers_, the lack or abundance of reputation does not check if someone is smart or stupid and you can see just how much I care about it by checking out my community wiki posts (no rep) or my bounties. I _am_ glad I ended up teaching you something though, I apologize if my voting offended you - it was not what I was going for and again, if you fix your code I will gladly revert it. – Benjamin Gruenbaum Sep 15 '15 at 14:47
  • @BenjaminGruenbaum I will definitely read through you post and improve mine. Thanks. – JM Yang Sep 15 '15 at 14:48
  • @BenjaminGruenbaum after reading your post I understand why you can't return an array. I don't think my suggestion is anti pattern because of the resolved data type. In respect to OP's original question, the resolved data type should be array, not arguments. We need to use another promise in order to convert arguments to array here. It's not anti pattern, a new promise introduce for purpose. The resolved datatype in my answer matches the OP's question better than yours. In addition, your supporter is down voting my other answers. What posts should I read for those down vote? – JM Yang Sep 15 '15 at 15:04
0

Please try this:

Service.prototype.parse = function(data) {
  var detailsArr = [];
  for (var i = 0; i < data.length; i++) {
    var details = new Details();
    detailsArr.push(details);
  }

  return $.when.apply($, detailsArr).then(function(){
    return Array.prototype.slice.call(arguments);
  });
};

Here we put all Details into an array, use $.when.apply($, arr) to wait for all Details get resolved. When it's done, each Details' return data will be passed to callback function as one parameter, so the call back function will receive total data.length number of parameters. Then we use Array.prototype.slice.call to convert all parameters to an array and return the result.

For your reference:

What does $.when.apply($, someArray) do?

How can I convert the "arguments" object to an array in JavaScript?

Community
  • 1
  • 1
JM Yang
  • 1,208
  • 9
  • 14
  • This is not antipattern. Please note in OP's question `How to wait for parse() to resolve everything and return a array?`, the return data type should be an array. A new promise is used here for converting the arguments object to array. You suggestion doesn't introduce a new promise but the return data type is not same as OP ask for. – JM Yang Sep 15 '15 at 15:21
  • 1
    I don't care what exactly you are doing in that callback, I care that you needlessly (and wrongly) create a new `$.Deferred` and resolve it from `.done()`, instead of just using `.then()`. Believe us, this is an antipattern :-) – Bergi Sep 15 '15 at 15:27
  • Your arguments thing can just be Array.of – Benjamin Gruenbaum Sep 15 '15 at 17:03
  • Thanks I learned Array.of from you but I prefer not to use it here also in my own project anytime soon because of it's compatibility. If there's a similar function in jQuery I would be glad to learn and use. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of – JM Yang Sep 15 '15 at 17:51