Как вы работаете с массивом отложенных jQuery?


132

У меня есть приложение, которое требует загрузки данных в определенном порядке: корневой URL-адрес, затем схемы, а затем, наконец, инициализация приложения с использованием схем и URL-адресов для различных объектов данных. По мере того как пользователь перемещается по приложению, объекты данных загружаются, проверяются на соответствие схеме и отображаются. Когда пользователь выполняет CRUD-данные, схемы обеспечивают проверку первого прохода.

У меня проблема с инициализацией. Я использую вызов Ajax для получения корневого объекта $ .when (), а затем создаю массив обещаний, по одному для каждого объекта схемы. Это работает. Я вижу выборку в консоли.

Затем я вижу выборку для всех схем, поэтому каждый вызов $ .ajax () работает. fetchschemas () действительно возвращает массив обещаний.

Однако последнее предложение when () никогда не срабатывает, и слово «DONE» никогда не появляется на консоли. Исходный код jquery-1.5, кажется, подразумевает, что "null" приемлем в качестве объекта для передачи в $ .when.apply (), так как when () создаст внутренний объект Deferred () для управления списком, если объект не прошел внутрь.

Это сработало с использованием Futures.js. Как следует управлять массивом отложенных jQuery, если не так?

    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);
        });
    });

У меня почти такая же проблема, за исключением того, что мне нужно запускать метод «успеха» для каждого запроса ajax в fetch_one до того, как будет напечатано «DONE». Как бы вы это сделали? Я пробовал использовать .pipe после "fetch_one", но это не сработало.
CambridgeMike

Ответы:


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чтобы thisвнутренняя $.whenссылка на jQuery. Это не должно иметь значения для источника, но лучше, чем передать null.

Изменил все ваши $ .ajax, заменив их, $.whenи образец работает

Так что это либо проблема в вашем запросе ajax, либо в массиве, который вы переходите к fetch_schemas.


Спасибо. Чем этот синтаксис отличается от done (). Fail ()?
Эльф Штернберг

2
@elf Sternberg, .then(a,b) === .done(a).fail(b)это ленивая стенография. Вы можете позвонить, .done(a).fail(b)если хотите
Raynos

1
О, и использование $ .when.apply ($, ...) и $ .when.apply (null, ...) кажется неуместным. Сам jQuery не имеет метода обещания (), поэтому он игнорируется в пользу созданного внутри объекта Deferred (jQuery 1.5, строка 943).
Эльф Штернберг

1
@ElfSternberg, это действительно не имеет значения, но для удобочитаемости мне не нужно бросать второй взгляд $.when.apply($, .... Это nullзаставляет меня «подожди, что?». Это вопрос стиля и практики кодирования. Мне пришлось прочитать источник, чтобы убедиться, thisчто внутри jQuery.when не будет пустая ссылка!
Raynos

7
Использование null заставляет меня думать: «Хорошо, это своего рода обходной путь» (что так и есть), тогда как если бы использовался $, мое внимание было бы отвлечено на размышления о том, для чего был $.
Даньял Айтекин

53

Приведенный выше обходной путь (спасибо!) Не решает должным образом проблему возврата объектов, предоставленных отложенному resolve()методу, потому что jQuery вызывает обратные вызовы done()и fail()с отдельными параметрами, а не с массивом. Это означает, что мы должны использовать argumentsпсевдо-массив, чтобы получить все разрешенные / отклоненные объекты, возвращаемые массивом отложенных, что некрасиво:

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

Поскольку мы передали массив deferred'ов, было бы неплохо получить обратно массив результатов. Также было бы неплохо получить реальный массив вместо псевдо-массива, чтобы мы могли использовать такие методы, как Array.sort().

Вот решение, вдохновленное методом when.js , when.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% уверены, что порядок элементов массива в переменной «schemas» в then () всегда будет в том же порядке, что и вызовы ajax в переменной «обещания» в переменной when ()?
netpoetica

6
Это должно быть просто встроено в jQuery, но - команда jQuery несколько раз отклоняла запрос. Между тем, люди продолжают задавать здесь вопрос и открывать аналогичные тикеты против jQuery, и в итоге мы получаем реализацию пользовательского пространства повсюду и / или неудобные призывы apply()... подумайте.
mindplay.dk

Спасибо за это решение! Есть ли способ получить успешные предметы, даже если один (или несколько) не удался?
doktoreas

ну, все, что вы здесь сделали, - это скрытые argumentsманипуляции в собственном методе. Отлично подходит для повторного использования, но не устраняет «уродство», с которым приходится иметь дело arguments(вы легко могли бы просто:var schemas=Array.prototype.slice.call(arguments);)
Cowbert

2
@crispyduck, не стоит deferred.fail(...)читать deferred.reject(...)?
Bob 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 или один из его потомков / имитаторов, уже некоторое время имеют доступ к этому оператору.
Эльф Штернберг,

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)
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.