3

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.

I am wanting to expand it to do some more Promises after the JSON data has loaded that are not AJAX requests.

For example after JSON data has loaded, I want to create another Promise which will run a function that will setup some DOM Event handlers/listeners.

After the function that sets up the Events handling, I would like to then run some other function after the events have finished being setup.

I am very new and learning Promises still and in some cases still learning JavaScript so I could use some help. An example code expanding mine below and on the JSFIddle would be very grateful for!

I believe if shown how to add 1 more promise to my demo then it will be enough for me to add as many as I need in my final app.

JSFIddle Demo of code below: http://jsfiddle.net/jasondavis/fttzoggj/


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];
};

// 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

  // I want to create another Promise here which will run a function that creates a bunch of DOM Event handlers/listeners and in that function when it is done loading it should fire a success like the above does when JSON data is done loading
})
.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.
});

enter image description here


UPDATE

I have updated my JSFiddle demo here http://jsfiddle.net/jasondavis/fttzoggj/2 adding a new function initDomEvents() and added it to a then(initDomEvents) call. MY console shows all is ran as I want it to however for some reason, now my error is triggered

JasonDavis
  • 48,204
  • 100
  • 318
  • 537
  • Since every `.then()` call returns a promise, you can chain `.then()`s til EOF. Just go ahead! – Bergi Oct 27 '15 at 18:37
  • @Bergi I read something about it returning the data from the previous promise gets passed into each `then()` and I get a little confused there as i'm not sure if I want that or not? I also always ready about how its meant for async function calls and in the new function where I want to init a bunch of events afrters my JSON data has loaded so when I hear async I think AJAX so wasnt 100% sure if I would simply make a regular function for my events and call `resolve()` in it...... – JasonDavis Oct 27 '15 at 18:45
  • @Bergi ...continued.... A littlem info on my app. It loads all sorts of project management related JSON data. Next it loads a Task JSON data for a task Modal window in which I have to setup all kinds of events and libraries for things like a datepicker plugins, some plugins for popovers for milestones, assigned users, markdown processding and a whole bunch more. I am just wanting to basically use the Promises to execute each section of this process in a special order so that certain things are not loaded before others are.... – JasonDavis Oct 27 '15 at 18:46
  • @Bergi ...continued...Example After I build my Task Modal and add to the DOM, sometimes my Datepicker library throws errors as it is ran before the library is fully loaded even. – JasonDavis Oct 27 '15 at 18:46
  • Maybe have a look at [my answer to "*Aren't promises just callbacks?*"](http://stackoverflow.com/a/22562045/1048572). Notice that promise callbacks can be synchronous as well, you don't *need* to return a promise (but you can, if you want to wait for something). "Setting up events" sounds synchronous, "loading a library" is async and should return a promise. – Bergi Oct 27 '15 at 18:59
  • A `.then(…).then(…)` chain perfectly models multiple async steps in a row, so if you have "sections" of that "loading process" it sounds fine to use a callback for each. – Bergi Oct 27 '15 at 19:00
  • @Bergi I think my end goal is something to do these steps each in a separate promise chained 1 after the next... (1) load JSON data (2) update task modal HTML in DOM by assigning JSON data for each value (3) setup all sorts of events to handle interaction with the task modal (edit in place, click events, keyboard events, textarea events, and many more) (4) also init a bunch of libraries (date picker, selection fileds, popovers, markdown, etc) (5) load task modal activities and comments panel – JasonDavis Oct 27 '15 at 19:07
  • @Bergi my demo the first call for RSVP I pass in my Promises object for the first one. If I chained `then()` I do not see where I would pass in the next promise? – JasonDavis Oct 27 '15 at 19:11
  • UPDATE I see another demo now...I see the parameter passed into `then('promise-returning-function')` can be the prmise function passed into it. MY demo has a var names results which threw me off – JasonDavis Oct 27 '15 at 19:17
  • I have updated my JSFiddle demo here http://jsfiddle.net/jasondavis/fttzoggj/2/ adding a new function `initDomEvents()` and added it to a `then(initDomEvents)` call. MY console shows all is ran as I want it to however for some reason, now my error is triggered – JasonDavis Oct 27 '15 at 19:31

2 Answers2

1

My console shows all is ran as I want it to however for some reason, now my error is triggered

There are no resolve and reject functions in your initDomEvents function. They are provided as the arguments to the new RSVP.Promise(…) callback only. Trying to call them throws an error, which is caught and propagated to your error handler (but it doesn't have a .statusText property, so only undefined is printed).

As I said in the comments, if your function does nothing asynchronous there is no need to return a promise. You just return a value or throw an exception as well from your promise callback.
If you insist on creating promises around your results or rejections, you can use the RSVP.Promise.resolve(…) or RSVP.Promise.reject(…) functions to create fulfilled/rejected promise objects.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    I also see now how a lot of my non ajax type code can simply be called inside the `then(function(results){ /* non async code */})` instead of being its own Promise function....I mention this in response to `function does nothing asynchronous there is no need to return a promise` I see what you mean now thanks – JasonDavis Oct 28 '15 at 01:46
0

Any specific reason why you're using RSVP? I'd switch to jQuery Promises.

var d = $.Deferred;

$.ajax({
 url: url,
 cache: true,
 dataType: 'json',
 success: function(data){
   d.resolve(data);
 },
 error: function(error){
   d.reject();
 }
});

$.when(d).then(function(data){
     //handle
});
Will
  • 546
  • 5
  • 16
  • 1
    I've read many articles about how jQuery got Promises wrong and in jQuery v3 they plan to correct and change them to be more ES6 compliant so I didnt wanna use a broken style just to have to change it a year down the road – JasonDavis Oct 27 '15 at 18:49
  • Also before using RSVP.js I used jQuery Promises. I've also ready many reviews where RSVP is supposed to be one of the best Promise libraries – JasonDavis Oct 27 '15 at 18:51
  • When I used jQuery Promises I actually preferred calling my own `d.resolve(data);` like your demo does but I kept being told that I shouldn't since the jQUery AJAX call does it already – JasonDavis Oct 27 '15 at 19:01
  • It could also be handled without the `d.resolve(data)` with some callback like `success: function(data){ callback(data) }, error: function(error){ callback(data) }`. For what you're trying to accomplish here I can't see jQuery causing you any problems. However I didn't actually answer your question :| . – Will Oct 27 '15 at 19:05