【问题标题】:How to use jQuery's deferred object with custom javascript objects?如何将 jQuery 的延迟对象与自定义 javascript 对象一起使用?
【发布时间】:2011-12-06 08:02:43
【问题描述】:

我有一个标准的 javascript 对象,它的原型使用 .start() 方法扩展,以 2 个回调作为参数:分别为 successfailure。此方法执行一些异步处理(它不是 AJAX),并根据此处理的结果调用成功或失败回调。

这是如何进行模式化的:

function MyObject() {
}

MyObject.prototype.start = function(successCallback, errorCallback) {
    (function(s, e) {
        window.setTimeout(function() {
            if (Math.random() < 0.8) {
                s();    
            } else {
                e();    
            }
        }, 2000);    
    })(successCallback, errorCallback);
}

在方法内部执行的确切处理并不重要,只是它是异步和非阻塞的。我无法控制 start 方法完成处理的时间点。我也无法控制这个方法的原型和实现。

我可以控制的是 successfailure 回调。由我来提供它们。

现在我有了这些对象的数组:

var arr = [ new MyObject(), new MyObject(), new MyObject() ];

这个数组中元素的顺序很重要。我需要在数组的每个元素上连续触发.start() 方法,但只有在前一个元素完成后(即调用成功回调)。如果发生错误(调用失败回调),我想停止执行并且不再对数组的其余元素调用 .start 方法。

我可以通过使用递归函数天真地实现这一点:

function doProcessing(array, index) {
    array[index++].start(function() {
        console.log('finished processing the ' + index + ' element');
        if (index < array.length) {
            doProcessing(array, index);
        }
    }, function() {
        console.log('some error ocurred');
    });
}

doProcessing(arr, 0);

这很好用,但是看看 jQuery 1.5 中引入的jQuery's deferred Object,我认为这段代码还有改进的余地。不幸的是,我对它还不是很舒服,我正在努力学习它。

所以我的问题是是否可以调整我的幼稚代码并利用这个新 API,如果可以,您能否提供一些建议?

这是一个 jsfiddle 我的实现。

【问题讨论】:

  • +1 总是很高兴看到顶级回答者提出一个好问题。自己回答这个?

标签: javascript jquery jquery-deferred


【解决方案1】:

你可以这样做:(jsFiddle)

function MyObject() {
}

MyObject.prototype.start = function(queue) {
    var deferred = $.Deferred();
    //only execute this when everything else in the queue has finished and succeeded
    $.when.apply(jQuery,queue).done(function() { 
        window.setTimeout(function() {
            if (Math.random() < 0.8) {
                deferred.resolve();    
            } else {
                deferred.reject();    
            }
        }, 2000); 
    });
    return deferred;
}

var arr = [ new MyObject(), new MyObject(), new MyObject() ];

var queue = new Array();
$.each(arr, function(index, value) {
    queue.push(value.start(queue)
        .done(function() {
           console.log('succeeded ' + index);
        })
        .fail(function() {
           console.log('failed ' + index);
        }));
});

不过,不太确定您是否会认为这是一种改进。

【讨论】:

  • 看起来很有趣,唯一的问题是我无法修改MyObject
  • 顺便问一下,你的最终目标是什么?延迟的真正力量在于您可以将多个回调注册到回调队列中。因此,无论 (a) 同步函数是否已经完成,您仍然可以注册额外的回调。此外,注册多个回调非常容易。这听起来像是您需要的东西吗?
  • 我真正的目标是通过尝试查看它是否可以应用于某些场景来了解延迟的工作原理。您的回答对这个方向非常有帮助。谢谢。
【解决方案2】:

您的实施没有任何问题。众所周知,使用 jQuery 并不总是最好的方法。

我会这样做:(无需修改 MyObject 类..)

function doProcessing(array, index) {
    var defer = new $.Deferred();

    $.when(defer).then(doProcessing);

    array[index++].start(function() {
        log('finished processing the ' + index + ' element');
        if (index < array.length) {
            defer.resolve(array, index);
        }
    }, function() {
        log('some error ocurred => interrupting the process');
    });
};

如您所见,与普通的 JavaScript 方法相比并没有真正的优势。 :)

这是我的小提琴:http://jsfiddle.net/jwa91/EbWDQ/

【讨论】:

    【解决方案3】:

    在我们编程时,记住 GRASP 原则或指南非常重要。

    http://en.wikipedia.org/wiki/GRASP_(object-oriented_design)

    获得高内聚和低耦合意味着我们的代码将更好、更可重用且更易于维护。

    因此,MyObject 类必须不知道队列的存在。 MyObject 会知道它自己的特性和方法等等。

    // Class MyObject
    
    function MyObject(name) {
        this.name = name;
    }
    
    MyObject.prototype.start = function() {
    
        var deferred = $.Deferred();
        var self = this;
        setTimeout(function() {
            if (Math.random() <= 0.8) {
                console.log(self.name + "... ok");
                deferred.resolve();
            } else {
                console.log(self.name + "... fail");
                deferred.reject();
            }
        }, 1000);
    
        return deferred.promise();
    } 
    

    main/caller 函数将知道 MyObject 的存在,它将创建三个实例,它们将按顺序执行。

    // Create array of instances
    var objectArray = [ new MyObject("A"), new MyObject("B"), new MyObject("C") ];
    
    // Create array of functions to call start function
    var functionArray = [];
    $.each(objectArray, function(i, obj) {
        functionArray.push(
            function() {
               return obj.start();
            }
        );
    });
    
    // Chain three start calls 
    $.iterativeWhen(functionArray[0], functionArray[1], functionArray[2])
    .done(function() {
    
        console.log("First: Global success");
    
        // Chain three start calls using array
        $.iterativeWhen.apply($, functionArray)
        .done(function() {
            console.log("Second: Global success");
        })
        .fail(function() {
            console.log("Second: Global fail");
        });
    
    })
    .fail(function() {
        console.log("First: Global fail");
    });
    

    我已经为 jQuery 构建了一个插件:iterativeWhen。它适用于 jQuery 1.8 及更高版本。

    $.iterativeWhen = function () {
    
        var deferred = $.Deferred();
        var promise = deferred.promise();
    
        $.each(arguments, function(i, obj) {
    
            promise = promise.then(function() {
                return obj();
            });
        });
    
        deferred.resolve();
    
        return promise;
    }; 
    

    Jsfiddle 在这里:http://jsfiddle.net/WMBfv/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-01
      相关资源
      最近更新 更多