Como você trabalha com uma matriz de adiados jQuery?

132

Eu tenho um aplicativo que exige que os dados sejam carregados em uma determinada ordem: a URL raiz, os esquemas e, finalmente, inicialize o aplicativo com os esquemas e URLs dos vários objetos de dados. À medida que o usuário navega no aplicativo, os objetos de dados são carregados, validados no esquema e exibidos. À medida que o usuário cria os dados, os esquemas fornecem validação de primeira passagem.

Estou tendo um problema com a inicialização. Eu uso uma chamada Ajax para buscar o objeto raiz, $ .when () e, em seguida, crio uma matriz de promessas, uma para cada objeto de esquema. Isso funciona. Eu vejo a busca no console.

Então, vejo a busca de todos os esquemas, para que cada chamada $ .ajax () funcione. fetchschemas () realmente retorna uma série de promessas.

No entanto, a cláusula final when () nunca é acionada e a palavra "DONE" nunca aparece no console. O código-fonte para o jquery-1.5 parece implicar que "nulo" é aceitável como um objeto para passar para $ .when.apply (), pois quando () criará um objeto Deferred () interno para gerenciar a lista se nenhum objeto for Transmitido.

Isso funcionou usando o Futures.js. Como deve ser gerenciada uma matriz de adiados jQuery, se não for assim?

    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);
        });
    });
Elf Sternberg
fonte
Eu tenho um problema quase idêntico, exceto que eu preciso disparar um método de "sucesso" para cada consulta ajax em fetch_one, antes que "DONE" seja impresso. Como você faria isso? Tentei usar o .pipe após "fetch_one", mas isso não pareceu funcionar.
CambridgeMike

Respostas:

198

Você está procurando

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

Isso também funcionará (para algum valor do trabalho, ele não corrigirá ajax quebrado):

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

Você vai querer passar em $vez de nullpara que thisdentro $.whense refira jQuery. Não deve importar para a fonte, mas é melhor do que passar null.

Zombou de todos os seus $ .ajax substituindo-os por $.whene a amostra funciona

Portanto, é um problema na sua solicitação ajax ou na matriz sua passagem para fetch_schemas.

Raynos
fonte
Obrigado. Qual é a diferença dessa sintaxe da done (). Fail ()?
Elf Sternberg
2
@elf Sternberg, .then(a,b) === .done(a).fail(b)é uma taquigrafia preguiçosa. Você pode ligar .done(a).fail(b)se quiser #
Raynos
1
Ah, e o uso de $ .when.apply ($, ...) e $ .when.apply (null, ...) parece ser irrelevante. O jQuery em si não possui um método promessa (), sendo ignorado em favor de um objeto Adiado gerado internamente (jQuery 1.5, linha 943).
Elf Sternberg
1
@ElfSternberg é realmente irrelevante, mas, para facilitar a leitura, não preciso dar uma segunda olhada $.when.apply($, .... O nullque me faz ir "espera, o que?". É uma questão de estilo e prática de codificação. Eu tive que ler a fonte para confirmar thisque não lançaria uma referência nula dentro do jQuery.when!
Raynos
7
O uso de null me faz pensar 'ok, isso é uma espécie de solução alternativa' (que é), enquanto que se $ fosse usado, minha atenção seria desviada para pensar em como o $ era.
Danyal Aytekin 05/10/12
53

A solução alternativa acima (obrigado!) Não resolve adequadamente o problema de retornar os objetos fornecidos ao resolve()método adiado porque o jQuery chama os retornos de chamada done()e fail()com parâmetros individuais, não uma matriz. Isso significa que precisamos usar o argumentspseudo-array para obter todos os objetos resolvidos / rejeitados retornados pelo array de adiados, o que é feio:

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

Como passamos uma série de diferidos, seria bom recuperar uma série de resultados. Também seria bom recuperar uma matriz real em vez de uma pseudo-matriz, para que possamos usar métodos como Array.sort().

Aqui está uma solução inspirada por when.js 's when.all()método que aborda estes problemas:

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

Agora você pode simplesmente passar uma matriz de adiamentos / promessas e recuperar uma matriz de objetos resolvidos / rejeitados em seu retorno de chamada, assim:

$.when.all(promises).then(function(schemas) {
     console.log("DONE", this, schemas); // 'schemas' is now an array
}, function(e) {
     console.log("My ajax failed");
});
Pato crocante
fonte
@crispyduck - você sabe se pode ter 100% de certeza de que a ordem dos elementos do array nos "schemas" var in then () sempre estará na mesma ordem em que o ajax chama no "promises" var no when ()?
Netpoetica 26/06
6
Isso deve ser incorporado ao jQuery, mas - a equipe do jQuery rejeitou a solicitação várias vezes. Enquanto isso, as pessoas continuam fazendo a pergunta aqui e abrindo tickets semelhantes contra o jQuery e terminamos com uma implementação de terra do usuário em todos os lugares e / ou chamadas desajeitadas para apply()... ir entender.
mindplay.dk
Obrigado por esta solução! Existe uma maneira de obter os itens de sucesso também se um (ou mais) falhar?
Doktoreas
bem, tudo o que você fez aqui é argumentsmanipulação oculta em seu próprio método. Ótimo para reutilização, mas não aborda a "feiúra" de ter que lidar arguments(você poderia facilmente ter:var schemas=Array.prototype.slice.call(arguments);)
cowbert 22/15/15
2
@crispyduck, não deveria deferred.fail(...)ler deferred.reject(...)?
Bob S
19

Se você estiver usando a versão ES6 do javascript Há um operador de propagação (...) que converte a matriz de objetos em argumentos separados por vírgula.

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

Saiba mais sobre o operador de propagação ES6 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator, encontre aqui

pashaplus
fonte
1
Sim. Embora aqueles que usam o Coffeescript ou um de seus descendentes / imitadores tenham tido acesso a esse operador por um tempo agora.
Elf Sternberg
0

estende quando com este código:

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)
    }
}
LIGUE-ME TZ
fonte