0

I have an array of promises and a function I need to call after the promises are complete. However, whenever a single promise fails out of the array, my $q.all(promise) will not launch the callback function.

  function searchLibraries(library) {
    //console.log(library);

    console.log('inside searchlibraries');
    var libraryCount = library.length;

    for (var i = 0; i < libraryCount; i++) {
      //console.log(i,library[i].siteUrl,library[i].listID,library[i].listName)
        itemPromise[i] = $().SPServices({
            operation: "GetListItems",
            webURL: library[i].siteUrl,
            listName: library[i].listID,
            CAMLViewFields: cViewFieldsLimited,
            CAMLQuery: cQueryAllCheckedOutDocuments,
            CAMLQueryOptions: cQueryOptions,
            cacheXML:true,                
            completefunc: function (xData,Status){
              if($(xData).hasSPError()){ 
                console.log("Error"); 
                console.log("Error Code:" ,$(xData).getSPErrorCode()); 
                console.log("Error Message:" ,$(xData).getSPErrorText()); 
              }
            }
        })
    }

    return $q.all(itemPromise).then(parseSearchResult);
  }
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Batman
  • 5,563
  • 18
  • 79
  • 155
  • Add a counter that increments when each promise is completed, regardless if it fails or succeeds. Each time you increment the counter, check if its equal to the length of the total promise count. If so, call `parseSearchResult` – Johan Oct 11 '14 at 22:53
  • See also [$q.all - take only those that resolved](http://stackoverflow.com/q/25296960/1048572) and those linked from there – Bergi Oct 12 '14 at 15:01

2 Answers2

5

.then() takes multiple arguments. The second argument is a function reference to be called if the promise fails. You can supply that second argument as in

$q.all(itemPromise).then(sucessHandler, failHandler)`

In the Q promise library, you might want to also use .allSettled() to get notified when all the promises have finished, even if some fail. Details for how that works are here. I use the Bluebird promise library which offers .settle().

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • so if I wanted to run the same function whether pass or fail I could do .then(parseResult,parseResult) ? – Batman Oct 11 '14 at 22:55
  • 1
    @Batman - yes, you could do that. Depending upon the promise library you are using, there may an even easier way, but that would make sure your function is called either way. – jfriend00 Oct 11 '14 at 22:56
  • +1 for answering OP's question. However, the obvious downside of this versus handling each promise individually would be that if one or more items fail, potentially successful items won't be parsed. – Johan Oct 11 '14 at 22:59
  • 1
    @Johan - Agree that that is an issue. The OP wasn't clear about what behavior they wanted in that regard and there is no code shown for `parseSearchResult()` so we couldn't really tell what they wanted. If it were my code, I'd probably want to use a different promise library function that would tell me when all requests had been completed and I could process just the successful ones. – jfriend00 Oct 11 '14 at 23:03
  • Yea it's fine. What happens is that the result will be missing some data but I prefer that then no data showing at all which is what was happening. – Batman Oct 11 '14 at 23:07
  • 1
    Quite logically but still worth mentioning is that $q.all will fail as soon as _one_ of its promises fails and doesn't wait the remaining ones to resolve. Just wrote this demo to play around with: http://jsfiddle.net/kw5vp0yz/ – Sebastian S Oct 11 '14 at 23:12
  • @Batman - In the Q promise library, you can use `.allSettled()` to get called when all promises have finished and then check to see which ones completed successfully. Details [here](https://github.com/kriskowal/q/wiki/API-Reference#promiseallsettled). – jfriend00 Oct 11 '14 at 23:12
  • @SebastianS - I've added a comment and some to my answer about the `.allSettled()` option which would get all promises whether succeed or fail, but requires some extra logic to test each result to see if it succeeded. I've used a similar feature in the Bluebird library (I'm not a Q user myself). – jfriend00 Oct 11 '14 at 23:14
  • 1
    @jfriend00 $q in angular offers only a subset of the functions of Q. afaik `allSettled()` isn't a part of it. – Sebastian S Oct 11 '14 at 23:18
  • Hi sorry, I'm looking at my application and I've noticed that something that was suppose to render only when all promises were completed is triggered much earlier, resulting in missing data...could this have something to do with the promises not 'settling'? – Batman Oct 11 '14 at 23:49
  • @Batman `.all()`will call the fail handler as soon as the first promise fails even when the others are not yet complete. That's what we've been talking about with `.settle()`. – jfriend00 Oct 11 '14 at 23:50
  • Oh okay I get it. That's a problem. Thanks for clearing that up, looks like I have to use Q or Bluebird than – Batman Oct 12 '14 at 00:00
  • @Batman - you can also just keep a count of how many requests have finished and call `parseSearchResult()` when the count gets to the final value. You don't have to go get a promise library just to solve this particular problem, though if you want a more advanced promise library, then `.allSettled()` in q or `.settle()` in Bluebird will help you here. – jfriend00 Oct 12 '14 at 00:06
  • ...can I mix promise libraries? I'm pretty comfortable with the use of $q and it seems like all I really need is the .settle(), could I implement bluebird just for that part and go back to using $q? – Batman Oct 12 '14 at 19:31
2

If you want to call parseSearchResult anyway — use always (http://api.jquery.com/deferred.always/) instead of then. But if you use it, you must catch errors in parseSearchResult.

Zav
  • 671
  • 3
  • 8