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)调用,如果它们的任何先例失败,则不应执行后两个调用。这是一个简化的示例,因此该解决方案应适用于更多数据点。

此外,在此示例中,没有从一个操作到下一个操作的数据流,所有数据一开始都是已知的,因此我也对数据流示例感兴趣。

我最近的尝试涉及泛化回调

// 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(
                                                  ...
                                                  )
                  )

我可以肯定的一件事是我做错了什么。如何在不堆积我的代码的情况下链接依赖项?对待我,就好像我对承诺不熟悉一样,因为我是。=)

更新

这是我想出的一个演练。这只是处理二传手。

当我们想要设置时,首先扩展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;
};

在共同点中,我们将函数和 Promise 联系起来,并且还创建了一种新的类型,.then()它返回馈入其中的 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

0

我不确定我是否完全理解,但这样的事情应该可以完成。

首先,两个实用功能:

function fbSet(path) {
    var dfrd = $.Deferred(),
        fbRef = new Firebase("https://my.firebaseIO.com/" + path);
    fbRef.set(data, function (error) {
       if (error) {
          dfrd.reject(fbRef, error);
       } else {
          dfrd.resolve(fbRef);
       }
    }
    return dfrd.promise();
}

function doFbChain(paths, word, type) {
    var dfrd = $.Deferred();
    $.each(paths, function(i, path) {
        path = path.replace('<word>', word).replace('<type>', type);
        dfrd = dfrd.then(fb(path));
    });
    return dfrd.promise();
}

现在,一些示例应用程序代码:

var myPaths = [
    '/words/upCase/<word>/id',
    '/words/upCase/<word>/type',
    '/words/<type>/<word>/id'
];
var myWord = 'myWord';
var myType = 'myType';

doFbChain(myPaths, myWord, myType).done(function() {
    //overall success
}).error(function(fbRef, error) {
    //failure somewhere along the way
});

您将需要使用代码以使其完全按照您的意愿运行。

于 2013-08-27T12:21:03.713 回答