javascript - mostrar - ¿Cómo se puede usar jQuery diferido?
ejecutar javascript despues de cargar pagina (11)
1) Utilícelo para garantizar una ejecución ordenada de devoluciones de llamada:
var step1 = new Deferred();
var step2 = new Deferred().done(function() { return step1 });
var step3 = new Deferred().done(function() { return step2 });
step1.done(function() { alert("Step 1") });
step2.done(function() { alert("Step 2") });
step3.done(function() { alert("All done") });
//now the 3 alerts will also be fired in order of 1,2,3
//no matter which Deferred gets resolved first.
step2.resolve();
step3.resolve();
step1.resolve();
2) Úsalo para verificar el estado de la aplicación:
var loggedIn = logUserInNow(); //deferred
var databaseReady = openDatabaseNow(); //deferred
jQuery.when(loggedIn, databaseReady).then(function() {
//do something
});
jQuery 1.5 trae el nuevo objeto diferido y los métodos adjuntos .Deferred
, .Deferred
y ._Deferred
.
Para aquellos que no han usado. .Deferred
antes, he anotado la fuente para it
¿Cuáles son los posibles usos de estos nuevos métodos? ¿Cómo hacemos para encajarlos en patrones?
Ya he leído la .Deferred y la source , así que sé lo que hace. Mi pregunta es ¿cómo podemos usar estas nuevas funciones en el código de todos los días?
Tengo un example simple de una clase de búfer que llama la solicitud de AJAX en orden. (Siguiente comienzo después de que termine uno anterior).
/* Class: Buffer
* methods: append
*
* Constructor: takes a function which will be the task handler to be called
*
* .append appends a task to the buffer. Buffer will only call a task when the
* previous task has finished
*/
var Buffer = function(handler) {
var tasks = [];
// empty resolved deferred object
var deferred = $.when();
// handle the next object
function handleNextTask() {
// if the current deferred task has resolved and there are more tasks
if (deferred.isResolved() && tasks.length > 0) {
// grab a task
var task = tasks.shift();
// set the deferred to be deferred returned from the handler
deferred = handler(task);
// if its not a deferred object then set it to be an empty deferred object
if (!(deferred && deferred.promise)) {
deferred = $.when();
}
// if we have tasks left then handle the next one when the current one
// is done.
if (tasks.length > 0) {
deferred.done(handleNextTask);
}
}
}
// appends a task.
this.append = function(task) {
// add to the array
tasks.push(task);
// handle the next task
handleNextTask();
};
};
Estoy buscando demostraciones y posibles usos de .Deferred
y .Deferred
.
También sería ._Deferred
ver ejemplos de ._Deferred
.
Enlazar a la nueva fuente jQuery.ajax
para obtener ejemplos es hacer trampa.
Recompensa: nos muestra qué técnicas están disponibles cuando retiramos si una operación se realiza de forma síncrona o asíncrona.
Acabo de usar Deferido en código real. En el proyecto jQuery Terminal , tengo la función exec que llama a los comandos definidos por el usuario (como si lo estuviera ingresando y presionando Intro), agregué los Aplazados a la API y llamé al exec con arreglos. Me gusta esto:
terminal.exec(''command'').then(function() {
terminal.echo(''command finished'');
});
o
terminal.exec([''command 1'', ''command 2'', ''command 3'']).then(function() {
terminal.echo(''all commands finished'');
});
los comandos pueden ejecutar código asíncrono, y exec debe llamar al código de usuario en orden. Mi primer api utiliza un par de llamadas de pausa / reanudación y en la nueva API las llamo automáticas cuando el usuario me lo promete. Así que el código de usuario solo puede usar
return $.get(''/some/url'');
o
var d = new $.Deferred();
setTimeout(function() {
d.resolve("Hello Deferred"); // resolve value will be echoed
}, 500);
return d.promise();
Yo uso código como este:
exec: function(command, silent, deferred) {
var d;
if ($.isArray(command)) {
return $.when.apply($, $.map(command, function(command) {
return self.exec(command, silent);
}));
}
// both commands executed here (resume will call Term::exec)
if (paused) {
// delay command multiple time
d = deferred || new $.Deferred();
dalyed_commands.push([command, silent, d]);
return d.promise();
} else {
// commands may return promise from user code
// it will resolve exec promise when user promise
// is resolved
var ret = commands(command, silent, true, deferred);
if (!ret) {
if (deferred) {
deferred.resolve(self);
return deferred.promise();
} else {
d = new $.Deferred();
ret = d.promise();
ret.resolve();
}
}
return ret;
}
},
dalyed_commands se usa en la función de reanudación que llama a exec nuevamente con todos los dalyed_commands.
y parte de la función de comandos (he eliminado partes no relacionadas)
function commands(command, silent, exec, deferred) {
var position = lines.length-1;
// Call user interpreter function
var result = interpreter.interpreter(command, self);
// user code can return a promise
if (result != undefined) {
// new API - auto pause/resume when using promises
self.pause();
return $.when(result).then(function(result) {
// don''t echo result if user echo something
if (result && position === lines.length-1) {
display_object(result);
}
// resolve promise from exec. This will fire
// code if used terminal::exec(''command'').then
if (deferred) {
deferred.resolve();
}
self.resume();
});
}
// this is old API
// if command call pause - wait until resume
if (paused) {
self.bind(''resume.command'', function() {
// exec with resume/pause in user code
if (deferred) {
deferred.resolve();
}
self.unbind(''resume.command'');
});
} else {
// this should not happen
if (deferred) {
deferred.resolve();
}
}
}
Aquí hay una implementación ligeramente diferente de un caché AJAX como en la respuesta de ehynd .
Como se señaló en la pregunta de seguimiento de fortuneRice, la implementación de ehynd en realidad no impidió varias solicitudes idénticas si las solicitudes se realizaron antes de que una de ellas hubiera regresado. Es decir,
for (var i=0; i<3; i++) {
getData("xxx");
}
probablemente resultará en 3 solicitudes AJAX si el resultado de "xxx" no se ha almacenado en caché anteriormente.
Esto se puede resolver almacenando en caché los aplazados de la solicitud en lugar del resultado:
var cache = {};
function getData( val ){
// Return a promise from the cache (if available)
// or create a new one (a jqXHR object) and store it in the cache.
var promise = cache[val];
if (!promise) {
promise = $.ajax(''/foo/'', {
data: { value: val },
dataType: ''json''
});
cache[val] = promise;
}
return promise;
}
$.when(getData(''foo'')).then(function(resp){
// do something with the response, which may
// or may not have been retreived using an
// XHR request.
});
El mejor caso de uso que se me ocurre es en el almacenamiento en caché de las respuestas AJAX. Aquí hay un ejemplo modificado de la publicación de introducción de Rebecca Murphey sobre el tema :
var cache = {};
function getData( val ){
// return either the cached value or jqXHR object wrapped Promise
return $.when(
cache[ val ] ||
$.ajax(''/foo/'', {
data: { value: val },
dataType: ''json'',
success: function( resp ){
cache[ val ] = resp;
}
})
);
}
getData(''foo'').then(function(resp){
// do something with the response, which may
// or may not have been retrieved using an
// XHR request.
});
Básicamente, si el valor ya se ha solicitado una vez antes, se devuelve inmediatamente del caché. De lo contrario, una solicitud AJAX obtiene los datos y los agrega al caché. El $.when
.then
/ .then
no se preocupa por nada de esto; todo lo que debe preocuparle es utilizar la respuesta, que se pasa al controlador .then()
en ambos casos. .when maneja una Promesa / Aplazada como Completada, ejecutando inmediatamente cualquier .done()
o .then()
en la cadena.
Los aplazados son perfectos para cuando la tarea puede o no funcionar de forma asíncrona, y desea abstraer esa condición del código.
Otro ejemplo del mundo real usando $.when
helper:
$.when($.getJSON(''/some/data/''), $.get(''template.tpl'')).then(function (data, tmpl) {
$(tmpl) // create a jQuery object out of the template
.tmpl(data) // compile it
.appendTo("#target"); // insert it into the DOM
});
Esta es una respuesta autopromocionante, pero pasé unos meses investigando esto y presenté los resultados en jQuery Conference San Francisco 2012.
Aquí hay un video gratis de la charla:
http://www.confreaks.com/videos/993-jqcon2012-i-promise-to-show-you-when-to-use-deferreds
La respuesta de ehynds no funcionará, ya que almacena en caché los datos de las respuestas. Debe almacenar en caché el jqXHR, que también es una promesa. Aquí está el código correcto:
var cache = {};
function getData( val ){
// return either the cached value or an
// jqXHR object (which contains a promise)
return cache[ val ] || $.ajax(''/foo/'', {
data: { value: val },
dataType: ''json'',
success: function(data, textStatus, jqXHR){
cache[ val ] = jqXHR;
}
});
}
getData(''foo'').then(function(resp){
// do something with the response, which may
// or may not have been retreived using an
// XHR request.
});
La respuesta de Julian D. funcionará correctamente y es una mejor solución.
Otro ejemplo de uso de Deferred
s para implementar un caché para cualquier tipo de computación (generalmente algunas tareas de alto rendimiento o de ejecución prolongada):
var ResultsCache = function(computationFunction, cacheKeyGenerator) {
this._cache = {};
this._computationFunction = computationFunction;
if (cacheKeyGenerator)
this._cacheKeyGenerator = cacheKeyGenerator;
};
ResultsCache.prototype.compute = function() {
// try to retrieve computation from cache
var cacheKey = this._cacheKeyGenerator.apply(this, arguments);
var promise = this._cache[cacheKey];
// if not yet cached: start computation and store promise in cache
if (!promise) {
var deferred = $.Deferred();
promise = deferred.promise();
this._cache[cacheKey] = promise;
// perform the computation
var args = Array.prototype.slice.call(arguments);
args.push(deferred.resolve);
this._computationFunction.apply(null, args);
}
return promise;
};
// Default cache key generator (works with Booleans, Strings, Numbers and Dates)
// You will need to create your own key generator if you work with Arrays etc.
ResultsCache.prototype._cacheKeyGenerator = function(args) {
return Array.prototype.slice.call(arguments).join("|");
};
Este es un ejemplo del uso de esta clase para realizar algunos cálculos (pesados simulados):
// The addingMachine will add two numbers
var addingMachine = new ResultsCache(function(a, b, resultHandler) {
console.log("Performing computation: adding " + a + " and " + b);
// simulate rather long calculation time by using a 1s timeout
setTimeout(function() {
var result = a + b;
resultHandler(result);
}, 1000);
});
addingMachine.compute(2, 4).then(function(result) {
console.log("result: " + result);
});
addingMachine.compute(1, 1).then(function(result) {
console.log("result: " + result);
});
// cached result will be used
addingMachine.compute(2, 4).then(function(result) {
console.log("result: " + result);
});
La misma memoria caché subyacente podría usarse para almacenar en caché las solicitudes Ajax:
var ajaxCache = new ResultsCache(function(id, resultHandler) {
console.log("Performing Ajax request for id ''" + id + "''");
$.getJSON(''http://jsfiddle.net/echo/jsonp/?callback=?'', {value: id}, function(data) {
resultHandler(data.value);
});
});
ajaxCache.compute("anID").then(function(result) {
console.log("result: " + result);
});
ajaxCache.compute("anotherID").then(function(result) {
console.log("result: " + result);
});
// cached result will be used
ajaxCache.compute("anID").then(function(result) {
console.log("result: " + result);
});
Puedes jugar con el código anterior en este jsFiddle .
Otro uso que he estado dando a un buen propósito es obtener datos de múltiples fuentes. En el ejemplo a continuación, obtengo múltiples objetos de esquema JSON independientes utilizados en una aplicación existente para validación entre un cliente y un servidor REST. En este caso, no quiero que la aplicación del lado del navegador comience a cargar datos antes de que tenga todos los esquemas cargados. $ .when.apply (). then () es perfecto para esto. Gracias a Raynos por los punteros sobre el uso de then (fn1, fn2) para monitorear las condiciones de error.
fetch_sources = 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);
}
var promises = fetch_sources(data[''schemas'']);
$.when.apply(null, promises).then(
function () {
var schemas = $.map(arguments, function (a) {
return a[0]
});
start_application(schemas);
}, function () {
console.log("FAIL", this, arguments);
});
Puede usar un objeto diferido para hacer un diseño fluido que funcione bien en los navegadores webkit. Los navegadores Webkit activarán el evento de cambio de tamaño para cada píxel en el que la ventana cambie de tamaño, a diferencia de FF e IE, que activan el evento solo una vez para cada tamaño. Como resultado, no tiene control sobre el orden en que se ejecutarán las funciones vinculadas al evento de cambio de tamaño de la ventana. Algo como esto resuelve el problema:
var resizeQueue = new $.Deferred(); //new is optional but it sure is descriptive
resizeQueue.resolve();
function resizeAlgorithm() {
//some resize code here
}
$(window).resize(function() {
resizeQueue.done(resizeAlgorithm);
});
Esto serializará la ejecución de su código para que se ejecute como usted lo intentó. Tenga cuidado con los escollos al pasar métodos de objetos como devoluciones de llamada a un aplazado. Una vez que dicho método se ejecuta como una devolución de llamada a diferido, la referencia ''this'' se sobrescribirá con referencia al objeto diferido y ya no se referirá al objeto al que pertenece el método.
También puede integrarlo con cualquier biblioteca de terceros que utilice JQuery.
Una de esas bibliotecas es Backbone, que en realidad apoyará a Aplazado en su próxima versión. Lo he hablado también en mi blog
Un diferido puede ser usado en lugar de un mutex. Esto es esencialmente lo mismo que los múltiples escenarios de uso de ajax.
MUTEX
var mutex = 2;
setTimeout(function() {
callback();
}, 800);
setTimeout(function() {
callback();
}, 500);
function callback() {
if (--mutex === 0) {
//run code
}
}
DIFERIDO
function timeout(x) {
var dfd = jQuery.Deferred();
setTimeout(function() {
dfd.resolve();
}, x);
return dfd.promise();
}
jQuery.when(
timeout(800), timeout(500)).done(function() {
// run code
});
Cuando utilice un Aplazado solo como exclusión mutua, tenga cuidado con los impactos en el rendimiento (http://jsperf.com/deferred-vs-mutex/2). A pesar de la conveniencia, así como los beneficios adicionales proporcionados por un aplazado, vale la pena, y en el uso real (basado en eventos impulsados por el usuario) el impacto en el rendimiento no debería ser notable.