如何使用自己的回调传递给延迟数组

时间:2016-07-17 15:46:35

标签: jquery promise deferred

是否可以使用自己的回调传递给$.when延迟数组?因此,我希望在解决所有延迟后调用每个回调,以便将它们传递给$.when

例如,在下面的示例中,在解决了适当的延迟后立即调用回调,并且在所有延迟完成后仅调用$.when中的一般回调

var d1 = $.Deferred();
var d2 = $.Deferred();

d1.done(function(result){alert('d1 is done');});
d2.done(function(result){alert('d2 is done');});

$.when(d1, d2).done(function(result){
alert('here only general callback is called');
});

d1.resolve();

setTimeout(function(){d2.resolve();}, 3000);

修改  我需要在哪里使用它。假设我有几个功能:

function SomeFunction1(){
var d = $.Deferred();

//if I define callback here it will call immediately after resolve
//d.done(function(result){
//// do something with result
//});

//$.ajax() or $.get() or $('#someID').load() or whatever else or just d.resolve
d.resolve('someData1');

return d.promise();
}

function SomeFunction2(){
var d = $.Deferred();

//if I define callback here it will call immediately after resolve
//d.done(function(result){
//// do something with result
//});

//$.ajax() or $.get() or $('#someID').load() or whatever else or just d.resolve
d.resolve('someData2');

return d.promise();
}

...

function SomeFunctionN(){
var d = $.Deferred();

//if I define callback here it will call immediately after resolve
//d.done(function(result){
//// do something with result
//});

//$.ajax() or $.get() or $('#someID').load() or whatever else or just d.resolve
d.resolve('someDataN');

return d.promise();
}

然后在某些情况下,我只需要调用其中一个函数,在其他情况下只调用其中两个函数,在第三种情况下它们都是:

1: $.when(SomeFunction1()).done(function(result){
//callback defined inside SomeFunction1
});

2: $.when(SomeFunction1(), SomeFunction2()).done(function(result){
//callbacks defined inside SomeFunction1 and SomeFunction2
});

3: $.when(SomeFunction1(), SomeFunction2(), ... , SomeFunctionN()).done(function(result){
//callbacks defined inside SomeFunction1, ... , SomeFunctionN
});

成功和出错时的回调应该在这些函数中定义,但只有在所有被调用的函数返回结果后才执行。

有可能吗?

2 个答案:

答案 0 :(得分:2)

由于这不是承诺中内置的任何行为,您可以实现自己的行为。当特定的承诺完成时,jQuery将调用.done()(而不是在完成组承诺时),因此您必须以不同的方式指定回调。

一种方案是为每个承诺添加.cb属性,当整个组完成后,将调用回调。然后,您可以创建$.when的超集,该超集将查找该回调并在整个组完成时调用它:

$.whenAfter = function(promiseArray){
    return $.when.apply($, promiseArray).then(function() {
        var results = Array.prototype.slice.call(arguments);
        promiseArray.forEach(function(p, index) {
            if (p.cb) {
                p.cb(results[index]);
            }
        });
        return results;
    });
}

或者,如果您不想将该属性添加到promise中,您可以传递一个与promiseArray对应的单独回调数组:

$.whenAfter = function(promiseArray, callbackArray){
    return $.when.apply($, promiseArray).then(function() {
        var results = Array.prototype.slice.call(arguments);
        promiseArray.forEach(function(p, index) {
            var cb = callbackArray[index];
            if (cb) {
                cb(results[index]);
            }
        });
        return results;
    });
}

如果您的回调必须保留在您的功能内部,并且在完成其他一系列操作之前您不想执行,那么我建议您传递承诺并执行这些回调当这个承诺得到解决时。

function SomeFunction1(p1){
     var p2 = $.ajax(...);
     // now wait for both our async operation and some other async operation
     // to be done before carrying out the rest of our business
     $.when(p1, p2).then(function(a1, a2) {
         // now everything else is done too so we can carry out the rest of our business
     });

     // return p2 so other things can know when this ajax operation is done
     return p2;
}

并且,你可以将这些中的多个结合起来:

var def = $.Deferred();
var p = def.promise();

$.when(SomeFunction1(p), SomeFunction2(p), SomeFunction3(p)).then(def.resolve, ref.reject);

我对承诺的体验告诉我这是一个丑陋的代码,但我目前还不确定如何使这种特殊类型的解决方案更清洁。

就个人而言,我认为我只是让SomeFunctionX同时返回一个promise和一个回调,因此可以从我们实际知道事情完成的外部调用回调:

function SomeFunction1(){
     var p = $.ajax(...);
     function callback() {
         // do something here after we're done and others are done too
     } 
     return {promise: p, callback: callback}
}

然后,在您要调用多个函数的位置,将它们放在一个数组中并迭代该数组,收集结果并在适当时调用回调:

  var funcs = [SomeFunction1, SomeFunction2, SomeFunction3];

  var callbacks = [];
  var promises = funcs.map(function(fn) {
      var retVal = fn();
      callbacks.push(retVal.callback);
      return retVal.promise;
  });
  $.when.apply($, promises).then(function() {
      var args = Array.prototype.slice.call(arguments);
      callbacks.forEach(function(cb, index) {
          cb(args[index]);
      })
  });

并且,您可以将其转换为可重用的函数,您只需传递一组返回正确数据结构的函数(promise和callback):

function runAll(funcs) {
    var callbacks = [];
    var promises = funcs.map(function (fn) {
        var retVal = fn();
        // if it only returns only a thenable (not our data structure), then just return the promise
        // this allows you to mix in functions that just return a promise
        if (typeof retVal.then === "function") {
            // assume no callback
            callbacks.push(null);
            return retVal;
        }
        callbacks.push(retVal.callback);
        return retVal.promise;
    });
    return $.when.apply($, promises).done(function () {
        try {
            var args = Array.prototype.slice.call(arguments);
            callbacks.forEach(function (cb, index) {
                if (cb) {
                    cb(args[index]);
                }
            });
        } catch(e) {
            // if any callback throws an exception, then reject
            return $.Deferred().reject(e);
        }
    });
});

var funcs = [SomeFunction1, SomeFunction2, SomeFunction3];

runAll(funcs).done(function(results) {
    // all done here
}).fail(function(err) {
    // some sort of error here
});

P.S。如果您正在使用已经创建并返回承诺(例如$.get())的异步操作,那么您不应该创建自己的延迟。那是promise anti-pattern。您应该只返回已经创建的承诺。

答案 1 :(得分:1)

您可以创建一个回调数组并循环遍历character

中的数组

$.when()
var funcs = {},
  promises = [],
  callbacks = [];
// create 5 functions and promises 
for (var i = 0; i < 5; i++) {

  (function(i) {
    funcs[i] = function() {
      console.log('Data in func #' + (i + 1) + ':', this.data)
    }
    promises[i] = $.Deferred();
    // push specific callback for each promise into array
    promises[i].then(function(result) {
      callbacks[i] = funcs[i].bind({
        data: result
      })
    });
    // random resolve times
    setTimeout(function() {
      promises[i].resolve('Promise #' + (i + 1))
    }, Math.random() * 1500);

  })(i)

}



$.when.apply(null, promises).done(function() {
  console.log('start callbacks loop');
  callbacks.forEach(function(fn) {
    fn();
  })
})