Як ви працюєте з масивом jQuery Deferreds?


132

У мене є додаток, який вимагає завантаження даних у певному порядку: коренева URL-адреса, потім схеми, потім нарешті ініціалізувати додаток із схемами та URL-адресами для різних об'єктів даних. Коли користувач орієнтується у програмі, об'єкти даних завантажуються, перевіряються у відповідності зі схемою та відображаються. Оскільки користувачі CRUD визначають дані, схеми забезпечують перевірку першого проходження.

У мене проблема з ініціалізацією. Я використовую виклик Ajax для отримання кореневого об'єкта $ .when (), а потім створюю масив обіцянок, по одному для кожного об'єкта схеми. Це працює. Я бачу підбір в консолі.

Потім я бачу доступ до всіх схем, тому кожен виклик $ .ajax () працює. fetchschemas () дійсно повертає масив обіцянок.

Однак той фінальний момент, коли () пункт ніколи не запускається, а слово "Зроблено" ніколи не з’являється на консолі. Вихідний код до jquery-1.5, мабуть, означає, що "null" є прийнятним як об'єкт для передачі до $ .when.apply (), як коли () створить внутрішній об'єкт Deferred () для управління списком, якщо жодного об'єкта немає передав.

Це працювало за допомогою Futures.js. Як слід керувати масивом jQuery Deferreds, якщо не так?

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

Це також буде працювати (для деякої вартості роботи він не виправить зламаний аякс):

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

Ви хочете пройти $замість nullтого, на що посилається thisвсередині . Це не має значення для джерела, але краще, ніж проходити .$.whenjQuerynull

Знущалися з усіх ваших $ .ajax, замінюючи їх $.whenна зразок і працює

Отже, це або проблема у вашому запиті на ajax, або у масиві, який ви переходите до fetch_schemas.


Дякую. Чим цей синтаксис відрізняється від done (). Fail ()?
Ельф Штернберг

2
@elf Sternberg, .then(a,b) === .done(a).fail(b)це лінива стенограма. Ви можете зателефонувати, .done(a).fail(b)якщо хочете
Райнос

1
О, і використання $ .when.apply ($, ...) та $ .when.apply (null, ...) видається неважливим. Сам jQuery не має методу обіцянки (), тому він ігнорується на користь внутрішньо генерованого об'єкта "Відкладений" (jQuery 1.5, рядок 943).
Ельф Штернберг

1
@ElfSternberg це справді не має значення, але для читабельності мені не потрібно другий погляд $.when.apply($, .... nullЗмушує мене йти «чекати, що?». Це питання стилю та практики кодування. Мені довелося прочитати джерело, щоб підтвердити, thisщо не кине нульове посилання всередині jQuery.when!
Райнос

7
Застосування нуля змушує мене думати: «нормально, це своєрідне вирішення» (що це таке), тоді як якби $ було використано, мою увагу було б перенаправлено на роздуми про wtf, для якого $.
Данял Айтекін

53

Вирішене рішення (спасибі!) Не належним чином вирішує проблему повернення об'єктів, наданих resolve()методу відкладеного, оскільки jQuery викликає зворотні done()та fail()виклики з окремими параметрами, а не масив. Це означає, що ми повинні використовувати argumentsпсевдомасив, щоб отримати всі вирішені / відхилені об’єкти, повернуті масивом відкладених, що некрасиво:

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

Оскільки ми пройшли масив відкладених, було б непогано повернути масив результатів. Було б також непогано повернути фактичний масив замість псевдомасиву, щоб ми могли використовувати такі методи 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% впевнені, що порядок елементів масиву у варі "схеми" в тоді () завжди буде в тому ж порядку, як виклик ajax у варі "vars" у, коли ()?
netpoetica

6
Це повинно бути просто вбудованим у jQuery, але - команда jQuery кілька разів відхиляла запит. Тим часом люди продовжують задавати це питання і відкриваємо подібні квитки на jQuery, і ми закінчуємо реалізацію користувальницької програми скрізь та / або незручні дзвінки, щоб apply()... зрозуміти.
mindplay.dk

Дякую за це рішення! Чи є спосіб отримати успішні предмети також, якщо один (або більше) не вдалося?
doktoreas

ну все, що ви тут зробили, - це прихована argumentsманіпуляція власним методом. Відмінно argumentsvar schemas=Array.prototype.slice.call(arguments);)
підходить

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.