0

私は Firebase を使用しており、一連の場所にデータを投稿するときに jQuery Deferred を使用しようとしています。投稿のいずれかが失敗した場合に、以前の投稿を回復し、場合によっては元に戻すメカニズムが必要です。

Firebase の投稿を行うための構文は次のとおりです。

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...
   }
}

一連の投稿をこのように書くのは自然なことです - それぞれが前の成功に依存する場合、コードのピラミッド化 AKA コールバック地獄になります。

私の具体的な例として、さまざまな場所に配置する必要がある「単語」に関するデータがあります。

/words/upCase/<word>/id =<word>
/words/upCase/<word>/type = <type>
/words/<type>/<word>/id = <word>

これらにはそれぞれ独自の Firebaseset(data,cb)呼び出しがあり、後者の 2 つは前例のいずれかが失敗した場合に実施しないでください。これは単純化された例であるため、ソリューションはより多くのデータ ポイントに対応する必要があります。

また、この例では、ある操作から次の操作へのデータの流れはなく、すべてのデータは最初からわかっているので、データの流れの例にも興味があります。

私の最新の試みには、コールバックの一般化が含まれます

// 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)();
  }
};

それから私はこのように呼びます

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

私の試みは、意図したとおりに依存関係を連鎖させていません。私が言いたいのは...

make(fbr,word).done(<new promise returning fn>).<get the done of that new promise>(...)

...次のようなピラミッド化なし:

make(fbr,word).done(
                  <new promise returning fn>.done(
                                                  ...
                                                  )
                  )

私が確信していることの1つは、私が何か間違ったことをしているということです。コードをピラミッド化せずに依存関係を連鎖させるにはどうすればよいですか? 私がプロミスに慣れていないかのように扱ってください。=)

アップデート

これが私が思いついたもののウォークスルーです。これはセッターのみを扱います。

最初Firebaseに、設定したいときに promisy 関数と呼ぶものを返すように拡張し.s()ます。ここで使用しているのは...

Firebase.prototype.s=function(v){
  var fn=makeSet(this,v);
  return fn;
};

ここにメーカーがあり、いくつかの共通点があります...

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

.then()共通点として、関数と promise をリンクし、それに供給された fn の promise を返す新しい種類の を作成します.ok()

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

その部分はおそらく冗長で、おそらく不確かですが、私の無知を露呈しています。に戻るsetFn():

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

緊張が高まります...今、私がそれを呼び出すとき、私はチェーンメカ​​ニズムを持っています:

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

クライアントからは、次のとおりです。

var ro=WordSvc.add(w,type);
ro.promise...// can attach handlers
ro.doIt();// kick it all off
4

1 に答える 1