1

I am not able to get chained promises to work as per RSVP documentation. I have a case where I am trying to fetch some data from the server. If for some reason an error occurs, I want to fetch the data from a local file.

I am trying to chain promises for that.

I have created a simplified example. The below example will give an output but is not what I want. http://emberjs.jsbin.com/cobax/3

App.IndexRoute = Em.Route.extend({
  model: function() {
    return Ember.$.getJSON('http://test.com/search')

    .then(undefined, function(errorObj, error, message) {
        return new Promise(function(resolve, reject) {
          resolve(model);
        }).then(function(response) {
          console.info(response.articles);
          return response.articles;
        });
    });
  }
});

This example is what I want but it wont call the final 'then'. http://emberjs.jsbin.com/cobax/3

App.IndexRoute = Em.Route.extend({
  model: function() {
    return Ember.$.getJSON('http://test.com/search')

    .then(undefined, function(errorObj, error, message) {
        return new Promise(function(resolve, reject) {
          resolve(model);
        });
    })

    .then(function(response) {
      console.info(response.articles);
      return response.articles;
    });
  }
});

Basically I want to handle the server/local response from the last 'then' method. I also want keep all the callbacks in a single level.

What is the error in the second code snipped?


Update

As @marcio-junior mentioned, the jquery deferred was the issue. Here is the fixed bin from him. http://jsbin.com/fimacavu/1/edit

My actual code doesn't return a model object, it makes another getJSON request to a json file. I can't replicate this in a bin as I dont think js bin allows us to host static files. Here is the code but it wont work. It fails due to some js error.

App.IndexRoute = Em.Route.extend({
  model: function() {
    var cast = Em.RSVP.Promise.cast.bind(Em.RSVP.Promise);
    return cast(Ember.$.getJSON('http://test.com/search'))

    .then(undefined, function(errorObj, error, message) {
        //return Em.RSVP.resolve(model);
        return cast(Ember.$.getJSON('data.json'));
    })

    .then(function(response) {
      console.info(response.articles);
      return response.articles;
    });
  }
});

Can you help me with this? These promises are a bit tricky to understand.

Here is the error stack I see

XMLHttpRequest cannot load http://test.com/search. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. localhost/:1
Error while loading route: index ember-canary-1.7.0.js:3916
logToConsole ember-canary-1.7.0.js:3916
defaultActionHandlers.error ember-canary-1.7.0.js:39681
triggerEvent ember-canary-1.7.0.js:39763
trigger ember-canary-1.7.0.js:42317
Transition.trigger ember-canary-1.7.0.js:42162
(anonymous function) ember-canary-1.7.0.js:42017
invokeCallback ember-canary-1.7.0.js:10498
publish ember-canary-1.7.0.js:10168
publishRejection ember-canary-1.7.0.js:10596
(anonymous function) ember-canary-1.7.0.js:15975
DeferredActionQueues.flush ember-canary-1.7.0.js:8610
Backburner.end ember-canary-1.7.0.js:8082
(anonymous function)
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
blessanm86
  • 31,439
  • 14
  • 68
  • 79
  • resolve inside 2nd then.. as simple as that.. – thecodejack Apr 17 '14 at 17:57
  • "It fails due to some js error." what JS error? Also, you don't need to cast the second `getJSON`, the fact it is returned from an RSVP then handler will cast it. – Benjamin Gruenbaum Apr 18 '14 at 01:04
  • @benjamin-gruenbaum I've added the error stack I get. I simply just took the model data in my bin example and saved it in a data.json file. – blessanm86 Apr 18 '14 at 02:30
  • @blessenm this is a cross origin policy error. Google the cross origin policy. – Benjamin Gruenbaum Apr 18 '14 at 11:07
  • @benjamin-gruenbaum The cross origin error is from the first getJSON. I know that, I want to get the second getJSON to work after the first one fails. The relevant error stack starts after the first line. I just copied everything printed in the console. Let me try to get an actual demo up. – blessanm86 Apr 18 '14 at 11:31
  • @benjamin-gruenbaum got it to work. It was malformed json in the external json file. It seems to be very difficult to debug promises. I really need a read the spec next. Thanks for all the help. I did learn a lot of new stuffs. – blessanm86 Apr 18 '14 at 13:08
  • 1
    @blessenm Did you try the promise tab in the Ember extension? – Benjamin Gruenbaum Apr 18 '14 at 13:09
  • @benjamin-gruenbaum Yes I did. Honestly I couldn't figure out a thing from that. I guess I should try out labeling promises and spend sometime with it to see what all information it gives. I just started with promises as ember kinda forces us which is a good thing. – blessanm86 Apr 18 '14 at 13:15

2 Answers2

3

You are returning a RSVP promise to a jquery deferred. And jquery deferreds doesn't have the feature of fulfill a rejected promise. So you need to update your sample to use Em.RSVP.Promise.cast(deferred), to transform a deferred in a RSVP promise, which implements the promises/a+ spec and does what you want:

App.IndexRoute = Em.Route.extend({
  model: function() {
    return Em.RSVP.Promise.cast(Ember.$.getJSON('http://test.com/search'))
      .then(undefined, function() {            
        return getDefaultData();
      })
      .then(function(response) {
        console.info(response.articles);
        return response.articles;
      });
  }
});

Your updated jsbin

Marcio Junior
  • 19,078
  • 4
  • 44
  • 47
  • You should `return model`, no need for the cast. Also, the second argument of `.then` has one parameter, not three. – Benjamin Gruenbaum Apr 17 '14 at 18:26
  • @marcio-junior Thanks, that modification did work. I knew Jquery deffered's are not the same but thought Ember.$ will be a modified version. But how would you handle a second getJSON request? I have modified my question to show some code. – blessanm86 Apr 18 '14 at 00:22
  • @marcio-junior Got my example to work finally. I'm adding another answer for future reference for others. Thanks for the help. – blessanm86 Apr 18 '14 at 13:09
  • Thanks @BenjaminGruenbaum. You're right, I did some copy paste mistakes. – Marcio Junior Apr 18 '14 at 16:47
  • Glad to help @blessenm, I recommend you to give a look in https://github.com/instructure/ic-ajax, it's more ember-friendly than $.ajax – Marcio Junior Apr 18 '14 at 16:48
0

Here is the final route code I used. Its simply checks for the my apps api for the results. If its not present, I take the static results from a sample json file. The parsing of the response happens at the end irrespective of where it came from.

var App.IndexRoute = Ember.Route.extend({
    model: function() {
        var cast = Em.RSVP.Promise.cast.bind(Em.RSVP.Promise);
        return  cast(Ember.$.getJSON('http://test.com/search'))

        .then(undefined, function(error) {
            console.info(error);
            return Ember.$.getJSON('assets/data.json');
        })

        .then(function(response) {
            console.info(response);
            return JSON.parse(response);
        });
    }
});
blessanm86
  • 31,439
  • 14
  • 68
  • 79