0

Disclaimer: I'm new to ES6 and promises in general so its possible my approach is fundamentally wrong.

The Problem

I have an api that returns paged data. That is it returns a certain number of objects for a collection and if there's more data it returns a Next property in the response body with the url to get the next set of data. The data will eventually feed the autocomplete on a text input.

To be specific, if my collection endpoint is called /Tickets the response would look like:

{
   Next: "/Tickets?skip=100&take=100"
   Items: [ ... an array of the first 100 items ... ]
}

My current solution to get all the ticket data would be to

  1. Make a new promise for the returning the whole combined set of data
  2. "Loop" the ajax calls by chaining dones until there is no more Next value
  3. Resolve the promise
getTickets(filterValue) {

    let fullSetPromise = new Promise();

    let fullSet = [];

    // This gets called recursively
    let append = function(previousResult) {
       fullSet.concat(previousResult.Items);

       // Loop!
       if(previousResult.Next) {
           $.ajax({
              url: previousResult.Next
           })
           .done(append)
           .catch(reason => fullSetPromise.reject(reason));
       }
       else {
          fullSetPromise.resolve(fullSet);
       }
    }

    // We set things off by making the request for the first 100
    $.ajax({
       url: `/Tickets?skip=0&take=100&filter=${filterValue}`
    })
    .done(append)
    .catch(reason => fullSetPromise.reject(reason));

    return fullSetPromise;
}

Eventually the promise is used by the frontend for autocomplete on a text input. Ideally I'd like to be able to abort the previous call when new input comes in.

inputChanged(e) {
   this.oldTicketPromise.abort();

   this.oldTicketPromise =   
        Api.GetTickets(e.target.value).then(updateListWithResults);
}

I am aware of debouncing. But that just means the problem happens every n seconds instead of on every key press.

I know the jqxhr object has an abort() property on it and I'd like that to be available to the caller somehow. But because there are multiple jqXHR objects used in GetTickets I'm not sure how to handle this.

So my main 2 questions are:

  1. What is the appropriate way to consume paged data from an api while returning a promise.
  2. How can the returned promise be made abortable?

Side question:

I feel like if I don't catch the errors then my "wrapper" promise will swallow any thrown errors. Is that a correct assumption?

Note the javascript code might have errors. It's mostly demonstrative for the logic.

Edit: Solved

I have solved this by combining this answer https://stackoverflow.com/a/30235261/730326 with an array of xhrs as suggested in the comments. I will add a proper answer with code when I find the time.

Community
  • 1
  • 1
jmathew
  • 1,522
  • 17
  • 29
  • 1
    You can push the ajax calls to an array, the call `.abort()` on each element of array. – guest271314 Nov 03 '16 at 23:20
  • 2
    ES6 promises aren't cancellable. You need to create extended Promise implementation that will have `cancel` or `abort` method. You may take a look at [Bluebird cancellation feature](http://bluebirdjs.com/docs/api/cancellation.html) for something that can be used out of the box. It should be noticed that you're using `fullSetPromise.resolve(fullSet)` wrong. You're trying to apply deferred pattern to ES6 promise. It doesn't work like that. – Estus Flask Nov 03 '16 at 23:52
  • [Promise's](https://promisesaplus.com/) are not abortable – Jaromanda X Nov 03 '16 at 23:52
  • 1
    Just because you have a promise of something, doesn't necessarily mean it's yours to cancel. Promises can be passed to other functions, which shouldn't implicitly be given the right to cancel your operation. – jib Nov 04 '16 at 03:01
  • I see. So maybe I shouldnt attempt to modify the promise structure. I'm actually not really tied to that idea. I just need some kind of way to cancel the ajax call cleanly when new user input comes in. – jmathew Nov 04 '16 at 03:22
  • @estus Thanks, I'll look into that. – jmathew Nov 04 '16 at 03:22
  • @guest271314 Thanks I'll try that as well. – jmathew Nov 04 '16 at 03:23
  • @jmathew See also http://stackoverflow.com/questions/32900103/abort-all-instances-of-xmlhttprequest/32900160#32900160 , http://stackoverflow.com/questions/27046787/abort-long-polling-ajax-request-and-restart-request-with-new-parameter/ – guest271314 Nov 04 '16 at 03:28

0 Answers0