Search code examples
javascriptpromisersvp.js

Why is my JavaScript Promises using the RSVP.js library returning an error after all my Promises succeeded?


The simple demo JavaScript code below uses the RSVP.js Promise Library https://github.com/tildeio/rsvp.js/ to load some JSON data using AJAX and on completion fires off some code after all JSON data has loaded.

After that it runs a non ajax function in a chained then() call

All my Promises are successful however it returns an error at the end as well!

I am not sure why the error is ran?

JSFiddle Demo : http://jsfiddle.net/jasondavis/fttzoggj/3/

var jsonPromiseCache = {};



// AJAX function to load JSON data using Promise()
var getJsonDataPromise = function(url, key) {

  if (!jsonPromiseCache[key]) {
     jsonPromiseCache[key] = new RSVP.Promise(function(resolve, reject){
      // If jsonPromiseCached data is not set then make AJAX requiest to get it


        var client = new XMLHttpRequest();
        client.open("GET", url);
        client.onreadystatechange = handler;
        client.responseType = "json";
        client.setRequestHeader("Accept", "application/json");
        client.send();

        console.log('---- "client" XMLHttpRequest/AJAX  variable ======= ',client);


        function handler() {
          if (this.readyState === this.DONE) {
            // On AJAX success, resolve() our Promise() and set result to cached variable
            // to avoid duplicate AJAX requests for this jsonCache[key] Data where "key"
            // is used to assign to each AJAX endpoint URL/request of JSON data...
            // (milestones, tasks, users, etc...)
            if (this.status === 200) {
                jsonPromiseCache[key] = this.response;

                console.log('---- jsonPromiseCache['+key+'] ====== ',jsonPromiseCache[key]);

                // Resolve() the Promise() on AJAX success
                resolve(this.response);

            // On AJAX failure, reject() our Promise()
            }else{
                reject(this);
            }
          }
        };

      // If JSON data for this key is already jsonPromiseCached, then return the jsonPromiseCached version
      // instead of making a new AJAX request!
    });
  }
  return jsonPromiseCache[key];
};

New non-ajax promise

// testing a  non ajax Promise
function initDomEvents() {
    var eventsLoaded = true;

    //if (eventsLoaded) {
        jsonPromiseCache['domevents'] = eventsLoaded;

        console.log('---- initDomEvents() ran');

        // Resolve() the Promise() 
        //resolve(jsonPromiseCache['domevents']);
        resolve();

        // On false, reject() our Promise()
    //}else{
    //    reject();
    //}

};

EXAMPLE USAGE DEMO

// usage loading JSON data with AJAX using Promises
var promises = {
    users: getJsonDataPromise('/echo/json/', 'users'),
    task: getJsonDataPromise('/echo/json/', 'task')
};


RSVP.hash(promises)
.then(function(results) {
  console.log('then() function ran on success of loading JSON data');
  console.log(results);
  console.log('results.users', results.users); // print the users JSON results
  console.log('results.task', results.task); // print the task JSON results
})
.then(initDomEvents)
.finally(function(){
  console.log('finally() function ran on success and failure.... It is always ran!');
})
.catch(function(reason){
  console.log('[ERROR] REASON:',reason.statusText); //if any of the promises fails.
});

UPDATE

This new demo http://jsfiddle.net/jasondavis/fttzoggj/6/ adds a 3rd Promise and the 3rd one does not get ran! THe 2nd Promise function initDOmEvents() returns this error now...

ERROR] REASON: ReferenceError: resolve is not defined
    at initDomEvents (http://fiddle.jshell.net/_display/:91:9)
    at G (http://apollowebstudio.com/labs/js/sugarcrm-pm/js/rsvp.min.js:2:6009)
    at F (http://apollowebstudio.com/labs/js/sugarcrm-pm/js/rsvp.min.js:2:5928)
    at L (http://apollowebstudio.com/labs/js/sugarcrm-pm/js/rsvp.min.js:2:6661)
    at MutationObserver.h (http://apollowebstudio.com/labs/js/sugarcrm-pm/js/rsvp.min.js:2:1667)

New code added...

// testing a non ajax Promise

function initTaskModalLibraries() {
    var taskModalScriptsLoaded = true;

    if (taskModalScriptsLoaded) {
        jsonPromiseCache['inittaskmodallibraries'] = taskModalScriptsLoaded;

        console.log('---- initTaskModalLibraries() ran');

        // Resolve() the Promise() 
        //resolve(jsonPromiseCache['domevents']);
        resolve();

        // On false, reject() our Promise()
    }else{
        reject();
    }

};

RSVP.hash(promises)
.then(function(results) {
  console.log('then() function ran on success of loading JSON data');
  console.log(results);
  console.log('results.users', results.users); // print the users JSON results
  console.log('results.task', results.task); // print the task JSON results
})
.then(initDomEvents)
.then(initTaskModalLibraries)
.finally(function(){
  console.log('finally() function ran on success and failure.... It is always ran!');
})
.catch(function(reason){
  console.log('[ERROR] REASON:',reason); //if any of the promises fails.
});

UPDATE 2

I see now in my new non-ajax functions I am missing new RSVP.Promise(function(resolve, reject){})

Fixed demo http://jsfiddle.net/jasondavis/fttzoggj/8/


Solution

  • In your [ERROR] log line, remove .statusText like so:

    .catch(function(reason){
      console.log('[ERROR] REASON:',reason); //if any of the promises fails.
    });
    

    you'll see this clickable error:

    [ERROR] REASON:
    ReferenceError: resolve is not defined       initDomEvents()/_display/ (line 92)
    G()                                          rsvp.min.js (line 2)
    F()                                          rsvp.min.js (line 2)
    L()                                          rsvp.min.js (line 2)
    h()                                          rsvp.min.js (line 2)
    

    Here's an updated fiddle that I think does what you intended. The main changes are:

    return new RSVP.Promise(initDomEvents)  
    

    inside your first then handler, which passes the returned result to the next then (chaining),

    and change the method signature of initDomEvents:

    function initDomEvents(resolve, reject) {
    

    so that resolve/reject are defined.