链接序列化延迟发布数据

时间:2013-08-26 12:15:48

标签: javascript jquery firebase jquery-deferred

当我将数据发布到一系列位置时,我正在使用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>

其中每个都有自己的Firebase set(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;
};

在共性中,我们链接函数和promises,并创建一种新的.then(),它返回fn的赋值,它被称为.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;
  };
}

紧张感......现在,当我打电话给我时,我有一个chainer机制:

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

1 个答案:

答案 0 :(得分: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
});

您需要使用代码来使其完全符合您的要求。