您如何使用jQuery Deferreds数组?


132

我有一个需要按一定顺序加载数据的应用程序:根URL,然后是架构,然后最终使用各种数据对象的架构和url初始化应用程序。当用户浏览应用程序时,数据对象将被加载,针对架构进行验证并显示。当用户对数据进行CRUD时,这些模式将提供首过验证。

我在初始化时遇到问题。我使用Ajax调用来获取根对象$ .when(),然后创建一个promise数组,每个模式对象一个。这样可行。我在控制台中看到了抓取。

然后,我将看到所有模式的访存,因此每个$ .ajax()调用均有效。fetchschemas()实际上确实返回了一个promise数组。

但是,最后的when()子句永远不会触发,并且“ DONE”一词永远不会出现在控制台上。jquery-1.5的源代码似乎暗示“空”可以作为传递给$ .when.apply()的对象,因为when()将构建内部Deferred()对象来管理列表(如果没有对象的话)通过了。

这使用Futures.js起作用。如果不是这样,应该如何管理jQuery Deferred数组?

    var fetch_schemas, fetch_root;

    fetch_schemas = function(schema_urls) {
        var fetch_one = function(url) {
            return $.ajax({
                url: url,
                data: {},
                contentType: "application/json; charset=utf-8",
                dataType: "json"
            });
        };

        return $.map(schema_urls, fetch_one);
    };

    fetch_root = function() {
        return $.ajax({
            url: BASE_URL,
            data: {},
            contentType: "application/json; charset=utf-8",
            dataType: "json"
        });
    };

    $.when(fetch_root()).then(function(data) {
        var promises = fetch_schemas(data.schema_urls);
        $.when.apply(null, promises).then(function(schemas) {
            console.log("DONE", this, schemas);
        });
    });

我几乎有一个完全相同的问题,除了我需要在打印“ DONE”之前为fetch_one中的每个ajax查询触发一个“成功”方法。您将如何去做?我尝试在“ fetch_one”之后使用.pipe,但这似乎不起作用。
CambridgeMike

Answers:


198

您正在寻找

$.when.apply($, promises).then(function(schemas) {
     console.log("DONE", this, schemas);
}, function(e) {
     console.log("My ajax failed");
});

这也将起作用(对于某些有价值的工作,它不会修复损坏的ajax):

$.when.apply($, promises).done(function() { ... }).fail(function() { ... });` 

您需要通过$而不是,null以便thisinside $.when指向jQuery。无关紧要的是源,但是最好通过null

用$替换掉所有$ .ajax $.when并且示例可以工作

因此,这可能是您的ajax请求中的问题,或者是您传递给fetch_schemas的数组中的问题。


谢谢。此语法与done()。fail()有何不同?
Elf Sternberg '02

2
@elf Sternberg,.then(a,b) === .done(a).fail(b)这是一个懒惰的速记。.done(a).fail(b)如果需要,可以致电
Raynos 2011年

1
哦,$。when.apply($,...)和$ .when.apply(null,...)的使用似乎无关紧要。jQuery本身没有promise()方法,因此它被忽略,而使用内部生成的Deferred对象(jQuery 1.5,第943行)。
Elf Sternberg

1
@ElfSternberg确实无关紧要,但是出于可读性考虑,我不需要再看一眼$.when.apply($, ...。该null让我走“等等,什么?”。这是样式和编码实践的问题。我必须阅读源代码以确认this不会在jQuery.when中抛出空引用!
雷诺斯

7
使用null使我觉得“好吧,这是一种解决方法”(是这样),而如果使用$,则我的注意力将转移到考虑wtf上。
Danyal Aytekin

53

上面的解决方法(谢谢!)无法正确解决取回提供给deferred resolve()方法的对象的问题,因为jQuery 使用单个参数而不是数组来调用done()fail()回调。这意味着我们必须使用arguments伪数组来获取由deferreds数组返回的所有已解析/拒绝的对象,这很丑陋:

$.when.apply($, promises).then(function() {
     var schemas=arguments; // The array of resolved objects as a pseudo-array
     ...
};

由于我们传入了一系列递延数据,因此最好返回一系列结果。取回实际数组而不是伪数组也很不错,因此我们可以使用像这样的方法Array.sort()

这里是通过激发溶液when.jswhen.all()方法,该方法解决了这些问题:

// Put somewhere in your scripting environment
if (jQuery.when.all===undefined) {
    jQuery.when.all = function(deferreds) {
        var deferred = new jQuery.Deferred();
        $.when.apply(jQuery, deferreds).then(
            function() {
                deferred.resolve(Array.prototype.slice.call(arguments));
            },
            function() {
                deferred.fail(Array.prototype.slice.call(arguments));
            });

        return deferred;
    }
}

现在,您可以简单地传入一组延迟/承诺,并在回调中返回一组已解析/被拒绝的对象,如下所示:

$.when.all(promises).then(function(schemas) {
     console.log("DONE", this, schemas); // 'schemas' is now an array
}, function(e) {
     console.log("My ajax failed");
});

@crispyduck-您是否可以100%确保then()中“ schemas”变量中数组元素的顺序始终与when中“ promises”变量中的ajax调用顺序相同()?
netpoetica 2014年

6
这应该只是内置在jQuery中,但是-jQuery团队多次拒绝了该请求。同时,人们一直在这里问这个问题,并针对jQuery开张类似的罚单,最终我们到处都是userland实现和/或笨拙的调用apply()... go Figure。
mindplay.dk 2014年

感谢您的解决方案!如果一个(或多个)失败,是否也有办法获得成功的项目?
doktoreas 2015年

好吧,您在这里所做的就是将隐藏的arguments操作隐藏到自己的方法中。非常适合重复使用,但不能解决必须处理的“丑陋” arguments(您可能很容易想到:var schemas=Array.prototype.slice.call(arguments);)
cowbert

2
@crispyduck,不应该deferred.fail(...)阅读deferred.reject(...)吗?
鲍勃·S

19

如果您使用的是JavaScript的ES6版本,则可以使用散布运算符(...),将对象数组转换为逗号分隔的参数。

$.when(...promises).then(function() {
 var schemas=arguments; 
};

有关ES6传播算子的更多信息https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator在这里找到


1
对。尽管我们当中那些使用Coffeescript或其后代/模仿者之一的人已经有一段时间可以使用该运算符了。
Elf Sternberg'Mar

0

使用以下代码扩展:

var rawWhen = $.when
$.when = function(promise) {
    if ($.isArray(promise)) {
        var dfd = new jQuery.Deferred()
        rawWhen.apply($, promise).done(function() {
            dfd.resolve(Array.prototype.slice.call(arguments))
        }).fail(function() {
            dfd.reject(Array.prototype.slice.call(arguments))
        })
        return dfd.promise()
    } else {
        return rawWhen.apply($, arguments)
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.