-3

I want to take the RSVP global object, and wrap it into a closure inside my global app closure, so that I can readily call it the way I want to call it. It also makes it easier to remember for me on how to use it, later in development.

Here's some background on that: RSVP library and a promise tutorial using it.

So given this standard usage of RSVP:

new RSVP.Promise(function(resolve, reject) {
  // succeed
  resolve(value);
  // or reject
  reject(error);
});

How can I create a wrapper for this, using the design pattern that you see below? I would like to get this RSVP object into the wrapper so that I don't have to instantiate a new RSVP Promise and define my callbacks all in the same place, every time.

I would instead like to simply call var newPromise = app.core.promise(callback) each time I want to make a new promise.

How can I wrap this into a closure so that's possible? This is what I have so far - what's the right way?

app.core.promise = function (resolveFunc, paramObj) {

    if(typeof(resolveFunc) === 'function'){

        //lets move on shall we?
        return new window.RSVP.Promise(function(resolveFunc, reject){

            //so this is the real question, what am i to do here?
            if(paramObj){
                //do something with this object first, or just run it, depending on your use case
                //paramObj = chopBalls(paramObj);
                resolveFunc(paramObj);
            } else {
                resolveFunc();
            }
            reject(error);
        });
    }
};

I realize doing this barely justifies, but look at the cool way I chain them below - I think it pays off.

Then I can just do this below to make the chains, which I think is a bit cleaner for what I need, which would be great, and is my goal!

var chain1, chain2;

    chain1 = app.core.promise(someSuccesCallback);

    chain1.then(function(){
        nowDoAChain1Operation();
    }) ;

    chain1.then(function(){
        nowFollowupOnChain1();
    }) ;

    chain1.catch(function(error){
        throw "we DO have an exception my friends in Chain1.  This is the master catch for the chain1.  Error object:" + error;    
    });

    chain2 = app.core.promise(aChain2OperationFunction);

    chain2.then(function(){
        anotherChain2Function();
    }) ;

    chain2.catch(function(error){
        throw "we DO have an exception my friends, in Chin2!  This is a master catch for the chain2.  Error object:" + error;
    });

Does it look familiar? It's the way standard deferreds are used, this is what I am trying to mimic, because I have been using standard jQuery Deferreds like that for some time now with great success.

app.core.promise = function(){
    return $.Deferred();  //ajax call promise
};

Is it possible what I'm trying to do, without defeating the purpose? (This is the design pattern I've chosen for the app. Does the design pattern make sense?).

EDIT Code Update @Benjamin,

so i would like to post my changes here, to see if this looks right. Notice how i do the last 3 returns here. Is this necessary in all cases? not right? It only depends on if you need the promise after the last return, or response is complete right? Or, does there always need to be a return?? Even in the render method which pushed forth an html response of its own? here's the e.g. code..

    //app.core.js
app.core.promise = function () {
    //lets move on shall we?
    return window.RSVP.Promise.resolve();
};

//app.ui.js
app.ui.showAmazonResults = function(){

    var promiseChain = app.core.promise(); //setup the promise

    promiseChain = promiseChain.then(function(){
        return app.ui.getSelectedIds(); //this runs a static function that might have a slow iteration
    });

    promiseChain = promiseChain.then(function(ids){
        app.ui.selectedIds = ids;  //just sets up a final property needed by the next call. 
        return app.convertData(); //this guy finishes the processing and setting up all the needed data fore the next call.
    });

    promiseChain = promiseChain.then(function(data){
        //all properties are clean and ready to be posted, run the 3rd party call..
        return app.amazon.fetchApi(); //this guy runs an api call out to a 3rd party
    });

    promiseChain = promiseChain.then( function(amazonData){
        //app.amazon.renderData(amazonData); //important to pass in null page here, so the dashboard and old results doesn't disappear on showing more results
        return app.amazon.renderData(amazonData); // this guy just renders this up front as rendered html, to the view.  so i dont know it needs to be returned
    });

    promiseChain = promiseChain.catch(function(errorResponse){
        //app.helper.log(errorResponse, 'myPage.this_ajax_call');
        return app.helper.log(errorResponse, 'myPage.this_ajax_call');
    });

    return promiseChain; //this entire methods jobs is to completly process and do the dirty work for the cals above, and also renders, so i dont know that this eitehr needs to be returned, since its parent may not need this converted to a promise.  or does it for whatever reason?
};

//somewhere inside an onready or init, where events are setup for the app all you need is after a user checks off items, then the following is ran on submit..  

app.ui.showAmazonResults();   

This is how i use it.

blamb
  • 4,220
  • 4
  • 32
  • 50
  • it looks like i see now how easy it might be to do this, would this work? `var chain1 = new Promise(function(resolve, reject) { resolve(); }); chain1.then(function(){ //do something });`? in ortherwords, instantiate it, then resolve it right away, this is your base ready to be chained. – blamb May 09 '15 at 06:29
  • 12
    Please don't shout. –  May 09 '15 at 17:43
  • I have another important question here, am i defeating the ability to be able to ever use resolve at a later point in the chain by doing this? i mean, is it defeating some of the default intentions doing it like this? if so, ill straighten out if i can find a good pattern to use this correctly. – blamb May 12 '15 at 03:57

1 Answers1

5

Well, promises are all about return values.

When you do:

chain1.then(function(){
    nowFollowupOnChain1();
});

You're not chaining the promise and not waiting for the operation inside nowFollowupOnChain1 - instead, you're branching the chain. What you probably want to be doing is to use the return value and to chain the promise:

chain1 = chain1.then(function(){
    return nowFollowupOnChain1();
});

Now any further thens to chain1 will wait for the operation to complete first. This is the point of chaining. You're typically supposed to convert whatever callback API you're using to return promises and then call the promise constructor very very rarely from that point on. Once you do that you can enjoy chaining which will take care of most of those issues for you.

If you want to create an empty resolved promise in RSVP you can always call RSVP.Promise.resolve() which will return a resolves RSVP promise. You can also do RSVP.defer() like you do in jQuery with $.Deferred() but I strongly suggest against it since $.Deferred sort of misses the point of promises.

Also, please consider not throwing strings, they won't have stack traces and will make your life really hard :)

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • ooh, i like this.. so far so good. the link especially i like, for converting. this is concrete evidence for me to now make sure that when ever i pass this stuff around, i will make sure its "converted" first. excellent concept that im taking on to help me master promises.. So, ill get right back with you though, because i had already figured out that i was chaining it wrong, i.e. `chain1 = chain1..` yet you did a good job if explaining it, but i think there is one more problem i wanted to adress on this. let me see if i can dig it up here.. ( i had a weekend go by since i worked onit..) – blamb May 11 '15 at 20:51
  • Hi, First some additional comments, thanks for the great answer. So as far as throwing, yes, i was just using string for an e.g., what i actually do is log it to database, and email, during development, or just log to db otherwise. (its a SPA, so i simply log the failed ajax calls, and the promise failed response info, along with a few app details) i want to say you were completely right that i wanted to use the return value and chain that. KUDOS there! I think this was my MAIN problem! – blamb May 12 '15 at 00:15
  • NOw, a question to you, on this portion of your response"RSVP.Promise.resolve()" so can i just do this? `app.core.promise = RSVP.Promise.resolve()` to set up the wrapper then? If so then THIS IS THE ANSWER i was looking for, because i wanted a simple way call it, if you will. so now im doing this, `return new window.RSVP.Promise(function(resolve){ resolve(1); });` its all i could make work. – blamb May 12 '15 at 00:19
  • oh hell yeah, thanks a million Benjamin, in your 2nd to last paragraph did it. This is what i was trying to figure out. short and sweet!! `app.core.promise = function () { //lets move on shall we? return window.RSVP.Promise.resolve(); };` superb! So i have RSVP fully implemented, and its working great! can you add that last code sampe to your answer, it looks cleaner? – blamb May 12 '15 at 00:21
  • @BrianThomas I think I explain it clearly with `RSVP.Promise.resolve()` but if you feel strongly that I should show how to create a function that returns it I can add it to the answer. I'm not sure it would make a substantial improvement though. By the way how did the question get so many downvotes in the first place? It's a reasonable question overall... – Benjamin Gruenbaum May 12 '15 at 00:30
  • HI ask my 'lowercase' fan club for that. I cant convince mods that its just my fanclub for all the biased downvotes. i think any logical conclusion would deem such. – blamb May 12 '15 at 00:37
  • Ok im going to add what i think it a good working solution that would be great to get general opinion on. see the [resolved] section of my post. – blamb May 12 '15 at 00:38