2011-02-02 9 views
128

Tengo una aplicación que requiere que los datos se carguen en un orden determinado: la URL raíz, luego los esquemas, y finalmente inicializan la aplicación con los esquemas y las direcciones URL para los diversos objetos de datos. A medida que el usuario navega por la aplicación, los objetos de datos se cargan, validan contra el esquema y se muestran. A medida que el usuario CRUDE los datos, los esquemas brindan validación de primer paso.¿Cómo se trabaja con una matriz de jQuery diferidos?

Tengo un problema con la inicialización. Uso una llamada Ajax para buscar el objeto raíz, $ .when(), y luego creo una matriz de promesas, una para cada objeto de esquema. Eso funciona. Veo la búsqueda en la consola.

Luego veo la búsqueda de todos los esquemas, por lo que cada llamada $ .ajax() funciona. fetchschemas() realmente devuelve una serie de promesas.

Sin embargo, esa cláusula when() final nunca se dispara y la palabra "DONE" nunca aparece en la consola. El código fuente de jquery-1.5 parece implicar que "nulo" es aceptable como un objeto para pasar a $ .when.apply(), como cuando() construirá un objeto interno Deferred() para administrar la lista si no hay ningún objeto pasado.

Esto funcionó usando Futures.js. ¿Cómo se debe gestionar una matriz de jQuery Deferreds, si no es así?

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

tengo casi un problema idéntico, excepto que necesito para disparar un método de "éxito" para cada consulta ajax en fetch_one, antes de imprimir "DONE". ¿Cómo harías para hacer esto? Intenté usar .pipe después de "fetch_one", pero eso no pareció funcionar. – CambridgeMike

Respuesta

187

Usted está buscando

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

Esto también funciona (para un cierto valor de la obra, que no van a resolver ajax rota):

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

Querrá pasar $ en lugar de null para que this dentro de $.when se refiera a jQuery. No debería importar a la fuente, pero es mejor pasar null.

burlado de toda su .ajax $ reemplazándolos con $.when y la muestra works

así que o un problema en su petición ajax o la matriz de su fallecimiento a fetch_schemas.

+0

Gracias. ¿En qué se diferencia esta sintaxis de done(). Fail()? –

+2

@elf Sternberg, '.then (a, b) === .done (a) .fail (b)' es una taquigrafía perezosa. Puede llamar a '.done (a) .fail (b)' si quiere – Raynos

+1

Ah, y el uso de $ .when.apply ($, ...) y $ .when.apply (null, ...) parece ser irrelevante jQuery en sí mismo no tiene un método promise(), por lo que se ignora a favor de un objeto diferido generado internamente (jQuery 1.5, línea 943). –

50

La solución anterior (gracias!) No aborda adecuadamente el problema de volver a los objetos proporcionados al resolve() método del diferido porque jQuery llama a los done() y fail() devoluciones de llamada con los parámetros individuales, no una matriz. Eso significa que tenemos que utilizar el arguments pseudo-matriz para obtener todos los objetos resueltos/rechazados devueltos por el conjunto de deferreds, que es feo:

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

desde que pasamos en una variedad de deferreds, sería bueno para recuperar una variedad de resultados. También sería bueno recuperar una matriz real en lugar de una pseudo-matriz para que podamos usar métodos como Array.sort().

Aquí hay una solución inspirada en cuando.js 's when.all() método que se ocupa de estos 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; 
    } 
} 

Ahora simplemente puede pasar en una variedad de deferreds/promesas y volver una serie de resueltos/rechazado objetos de la devolución de llamada, así:

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

Guau, esto funciona perfectamente; gran idea. ¡Gracias! –

+0

@crispyduck - ¿sabes si puedes estar 100% seguro de que el orden de los elementos de la matriz en la var de "esquemas" en entonces() siempre estará en el mismo orden que las llamadas ajax en la var de "promesas" en el cuando()? – netpoetica

+1

esto me salvó el culo, gracias! –

17

Si está utilizando la versión ES6 de javascript Existe un operador de dispersión (...) que convierte una matriz de objetos en argumentos separados por comas.

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

Más acerca operador de difusión ES6 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator encontrar aquí

+0

Sí. Aunque aquellos de nosotros que usamos Coffeescript o uno de sus descendientes/imitadores hemos tenido acceso a ese operador por un tiempo. –

+0

¿no puedes expandir tus esquemas también? – matthewdaniel

0

se extiende cuando con 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) 
    } 
} 
Cuestiones relacionadas