Pergunta

Re: https://github.com/tildeio/rsvp.js

What's the best way to integrate RSVP.js with jQuery's $.ajax? After doing some reading and research, I see that in Ember there is active development of a wrapper around it... https://github.com/emberjs/ember.js/pull/4148

Anyone with experience with this use-case of promises?

I'm using RSVP.js because I'm handling a dynamic array of promises through the .all() method.

Each of those promises from the array has its own promise that is fulfilled once its done polling for data from a function recursively. I'm having issues considering how I should structure the code.

The use-case if you're interested is that I'm sending an AJAX request to my API for a report pertaining to a specific resource that returns a list of URL endpoints that it should hit for more report data about its child resources. Each of those resources then returns a JSON object with report data for a specific day and another URL with params for the next day (or group of days). This then keeps polling for data with "next" from the same endpoint until there's nothing left.

Thanks in advance to anyone who can help!

Also, if you have any guidance on how to format this code so it's more readable and maintainable, I'd love to hear.

Code:

url = "http://localhost:3000/api/foo_resources/1/reports/bar"

var headers = {
    "Accept": 'application/vnd.xps+json; version=1', // Headers for API access
    "X-User-Email": 'example@company.com',
    "X-User-Token": '1234abcd',
}

$.ajax({
    type: 'GET',
    url: url,
    headers: headers,
    dataType: 'json',
    xhrFields: {
        withCredentials: true
    }
}).then(function(response) {

    // the ajax request would return a long list of bucket like this with endpoints to hit {
    // {
    //   "buckets": [
    //     "http://localhost:3000/api/foos_nested_resources/1/reports/bar"
    //     "http://localhost:3000/api/foos_nested_resources/2/reports/bar"
    //     "http://localhost:3000/api/foos_nested_resources/3/reports/bar"
    //   ]
    // }

    var promises = response.buckets.map(function getReportData(bucket) {

        return new RSVP.Promise(function(resolve, reject) {
            var recursiveRequest = function(bucket) {
                $.ajax({
                    type: 'GET',
                    url: bucket,
                    headers: headers,
                    dataType: 'json',
                    xhrFields: {
                        withCredentials: true
                    }

            // This is the report that comes back, obviously truncated significantly for this example
            // {
            //   reports: [
            //     { id: 1, datapoint_a: 5438, datapoint_b: 1389 },
            //     { id: 2, datapoint_a: 4336, datapoint_b: 2236 }
            //   ],
            //   next: "http://localhost:3003/api/nexted_foo_resources/1/reports/bar?ends=2014-02-06&starts=2014-01-24"
            // }

                }).done(function(response) {
                    $.merge(reports, response.reports); // I could use concat here but I prefer readability

                    if (response.next) {
                        recursiveRequest(response.next);
                    } else {
                        resolve(reports);
                    }
                }).error(function(error) {
                    reject(error);
                });
            };

            recursiveRequest(bucket);
        });
    });

    RSVP.all(promises).then(function(data) {
        console.dir(data);
    }).catch(function(error) {
        console.dir(error);
    })
})
Foi útil?

Solução

With the caveat that I have not tested this code (since I don't have your API handy), I think something like the following would be closer to idiomatic usage of RSVP:

var initialUrl = "http://localhost:3000/api/foo_resources/1/reports/bar";

var headers = {
  "Accept": 'application/vnd.xps+json; version=1', // Headers for API access
  "X-User-Email": 'example@company.com',
  "X-User-Token": '1234abcd',
};

function rsvpAjax(opts){
  return new RSVP.promise(function(resolve, reject){
    var defaultOpts = {
      type: 'GET',
      headers: headers,
      dataType: 'json',
      xhrFields: {
        withCredentials: true
      }
    };
    $.ajax($.extend({}, defaultOpts, opts, {
      success: function(json) {
        resolve(json);
      },
      error: function(jqXhr, textStatus, errorThrown){
        reject({ jqXhr: jqXhr, textStatus: textStatus, errorThrown: errorThrown});
      }
    }));
  });
}

function requestBucket(bucket){
  return rsvpAjax({ url: bucketUrl }).then(bucketResponseProcessor(bucket));
}

function bucketResponseProcessor(bucket){
  return function(response){
    $.merge(bucket.reports, response.reports);
    if (response.next) {
      bucket.url = response.next;
      return requestBucket(bucket);
    } else {
      return bucket.reports;
    }    
  };
}

rsvpAjax({ url: initialUrl }).then(function(response) {
  return RSVP.all(response.buckets.map(function(bucketUrl){
      var bucket = { url: bucketUrl, reports: [] };
      return requestBucket(bucket).then(processBucketResponse);
  }));
}).then(function(reports) {
  console.dir(data);
}).catch(function(error) {
  console.dir(error);
});

Outras dicas

I think what you are looking for is Ember.RSVP.Promise.cast()

The examples show it with $.getJSON(), but it should work with any jQuery Ajax method, as they all return Deferred objects.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top