Note that there are some explanatory texts on larger screens.

plurals
  1. POChaining serialized Deferred for posting data
    primarykey
    data
    text
    <p>I am using Firebase and attempting to use jQuery Deferred when I post data to a series of locations. I want a mechanism where if any of the posts fail, I want to recover and possibly undo previous posts.</p> <p><strong>The syntax for doing a Firebase post is</strong></p> <pre><code>var fbRef = new Firebase("https://my.firebaseIO.com/path/to/data"); fbRef.set(data,function (error){ if (error){ // do something about it } else { // all went well // next post would go here... } } </code></pre> <p>Naturally writing a series of posts this way - where each depends on the success of the previous will result in the pyramiding of code AKA callback hell.</p> <p><strong>For my concrete example, I have data about 'words' that need to be put at various locations</strong></p> <pre><code>/words/upCase/&lt;word&gt;/id =&lt;word&gt; /words/upCase/&lt;word&gt;/type = &lt;type&gt; /words/&lt;type&gt;/&lt;word&gt;/id = &lt;word&gt; </code></pre> <p>Each of these will have its own Firebase <code>set(data,cb)</code> call, and the latter two should not be conducted should any of their precedents fail. This is a simplified example, so the solution should be geared to many more data points.</p> <p>Also, in this example there is no flow of data from one operation to the next, all data is known at the start, so I am also interested in data flow examples.</p> <p><strong>My latest attempt involves genericizing the callback</strong></p> <pre><code>// function factory. function will set firebase reference value and handle the deferred if exists var makeFn=function (fbr,val,task){ return function (){ fbr.set(val,function (err){ if (err) { console.log(fbr.toString()+' Error in Firebase:'+err.toSource()); if (task) { task.reject(err); } } if (task) { task.resolve(fbr); } }); if (task) { return task.promise(); } } } // factory that returns a function that returns a promise about setting data var make=function (fbr,val){ return function (){ var task=new $.Deferred(); return makeFn(fbr,val,task)(); } }; </code></pre> <p><strong>Then I call like this</strong></p> <pre><code>var fbr = new Firebase("https://my.firebaseIO.com/words/upCase/" + word + "/id"); make(fbr,word)() // set id promise .done(makeFn(fbr.parent().child('type'),type)) // HERE, not right .done(makeFn(fbr.parent().parent().parent().child(type).child(word).child('id'),word)) // set third data, still not right .done(function(){ //inform all went well } ); </code></pre> <p>My attempt is not chaining dependencies as I intend. What I want to say is...</p> <pre><code>make(fbr,word).done(&lt;new promise returning fn&gt;).&lt;get the done of that new promise&gt;(...) </code></pre> <p>... without pyramiding like:</p> <pre><code>make(fbr,word).done( &lt;new promise returning fn&gt;.done( ... ) ) </code></pre> <p>The one thing I am sure of is that I am doing something wrong. How do I chain dependencies without pyramiding my code? Treat me as if I am new to promises, because I am. =)</p> <p><strong>UPDATE</strong></p> <p>Here is a walk-through of what I've come up with. This just deals with setters.</p> <p>First extend <code>Firebase</code> to return what I call a promisy function when we want to set, I am just using <code>.s()</code> here...</p> <pre><code>Firebase.prototype.s=function(v){ var fn=makeSet(this,v); return fn; }; </code></pre> <p>Here is the maker, it has some commonalities...</p> <pre><code>var makeSet=function (fbr, val){ // factory that returns a function that returns a promise about setting data var task=Q.defer(); var fn=setFn(fbr,val,task); makeCOMMON(task,fn,fbr); return fn; }; </code></pre> <p>In the commonalities, we link functions and promises, and also create a new kind of <code>.then()</code> that returns the promise of the fn fed into it, its called <code>.ok()</code>.</p> <pre><code>var makeCOMMON=function (task, fn, fbr){ var p=task.promise; fn.task=task; fn.fbr=fbr.toString(); fn.p=p; p.fbr=fbr.toString(); p.fn=fn; p.ok=function (fnn){ this.then(fnn); return fnn.p; }; } </code></pre> <p>That part is probably redundant, and maybe iffy, it exposes my ignorance. Back to <code>setFn()</code>:</p> <pre><code>var setFn=function (fbr, val, task){ // function factory. function will set firebase reference value and handle the deferred if exists return function (){ task.notify('Setting '+fbr.toString()+' to '+val.toSource()); fbr.set(val,function (err){ if (err) { task.notify('Rejecting Update due to error '+fbr.toString()); task.reject({ error:err, FbPath:fbr.toString(), FbTask:task, FbVal:val, toString:function(){ var s=''; s+=' '+this.error.toString(); s+=' '+this.FbPath; if (this.FbVal) { s+=' '+this.FbVal.toSource(); }else{ s+=' null'; } return s; } });//reject }else{ task.resolve(fbr); } }); return task.promise; }; } </code></pre> <p>The tension builds... now, when I go to call that, I have a chainer mechanism:</p> <pre><code>add:function(w,type){ w=w.toLowerCase(); var fn0,fnLast; var fbr=FirebaseSvc.get(); var proms=[]; // var chain=function(fbrFn){ var wasFirst=false; if (typeof fn0=='undefined') { wasFirst=true; fn0=fbrFn; } proms.push(fbrFn.p); fbrFn.p.progress(logProgress); fbrFn.p.fail(logFail); if (!wasFirst) { var rv=fnLast.p.ok(fbrFn); fnLast=fbrFn; return rv; }else{ fnLast=fbrFn; return fbrFn.p; } }; // //.c() is .child() alias chain(fbr.root().c('words/upCase/'+w+'/id').s(w));//This is returing a promise, so I can still do promisy things, but I am not right now chain(fbr.root().c('words/upCase/'+w+'/type').s(type)); chain(fbr.root().c('words/words'+type+'/'+w+'/id').s(w)); // var ro={ promise:Q.all(proms), doIt:fn0 }; return ro; },//.add </code></pre> <p>From the client, it is:</p> <pre><code>var ro=WordSvc.add(w,type); ro.promise...// can attach handlers ro.doIt();// kick it all off </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload