funciones - promesas javascript
¿Cómo puedo encadenar tres llamadas asincrónicas utilizando las promesas de jQuery? (10)
En cada caso, devuelva el objeto jqXHR devuelto por $.ajax()
.
Estos objetos son compatibles con Promise, por lo que se pueden encadenar con .then()
/ .done()
/ .fail()
/ .always()
.
.then()
es el que desea en este caso, exactamente como en la pregunta.
function first() {
return $.ajax(...);
}
function second(data, textStatus, jqXHR) {
return $.ajax(...);
}
function third(data, textStatus, jqXHR) {
return $.ajax(...);
}
function main() {
first().then(second).then(third);
}
Los data
Argumentos, textStatus
y jqXHR
surgen de la llamada $.ajax()
en la función anterior, es decir. first()
alimenta el second()
y el second()
feeds third()
.
DEMO (con $.when(''foo'')
para entregar una promesa cumplida, en lugar de $.ajax(...)
).
Tengo tres llamadas HTTP que necesito hacer de forma síncrona y ¿cómo paso los datos de una llamada a la otra?
function first()
{
ajax()
}
function second()
{
ajax()
}
function third()
{
ajax()
}
function main()
{
first().then(second).then(third)
}
Traté de usar el diferido para las dos funciones y se me ocurrió una solución parcial. ¿Puedo extenderlo para tres funciones?
function first() {
var deferred = $.Deferred();
$.ajax({
"success": function (resp)
{
deferred.resolve(resp);
},
});
return deferred.promise();
}
function second(foo) {
$.ajax({
"success": function (resp)
{
},
"error": function (resp)
{
}
});
}
first().then(function(foo){second(foo)})
En realidad, hay un enfoque mucho más fácil al usar promesas con jQuery. Eche un vistazo a lo siguiente:
$.when(
$.ajax("/first/call"),
$.ajax("/second/call"),
$.ajax("/third/call")
)
.done(function(first_call, second_call, third_call){
//do something
})
.fail(function(){
//handle errors
});
Simplemente encadena todas tus llamadas a la llamada $ .cuando (...) y gestiona los valores devueltos en la llamada .done (...).
Aquí hay un tutorial si lo prefiere: http://collaboradev.com/2014/01/27/understanding-javascript-promises-in-jquery/
Encontré una buena solución aquí: ¿Cómo encadenar una secuencia de funciones diferidas en jQuery 1.8.x?
Y aquí está mi propia implementación de un enfoque similar, algo feo pero probablemente funcione. Emite el resultado de cada método como una «actualización de progreso» en el objeto prometido devuelto.
$.chain = function() {
var def = $.Deferred();
var funcs = arguments;
var left = arguments.length;
function next() {
if(left == 0) {
def.resolve();
return;
}
var func = funcs[funcs.length - left]; // current func
var prom = func(ret).promise(); // for promise will return itself,
// for jquery ojbect will return promise.
// these handlers will be launched in order we specify them
prom.always(function() {
left--;
}).done(function(ret) {
def.notify({
idx: funcs.length-left,
left: left,
result: ret,
success: true,
});
}).fail(function(ret) {
def.notify({
idx: funcs.length-left,
left: left,
result: ret,
success: false,
});
}).always(function(ret) {
next(ret);
});
}
next();
return def.promise();
};
¿Cómo usarlo para su situación? Tal vez no es hermoso, pero debería funcionar:
function first() {
return ajax(...);
}
var id;
funciton second() {
return ajax(id, ...);
}
function third() {
return ajax(id, ...);
}
$.chain(first, second, third).progress(function(p) {
if(p.func == first)
id = p.result.identifier;
}).then(function() {
alert(''everything is done'');
});
O simplemente puede asignar esa variable de identificación desde la first
función.
O si solo necesita el resultado de la función anterior, puede usar este enfoque:
function first() {
return ajax(...);
}
function second(first_ret) {
return ajax(first_ret.id, ...);
}
function third(second_ret) {
return ajax(second_ret.something, ...);
}
Es bastante tarde para responder, pero creo que a las respuestas les falta un código directo para encadenar. Encadenar eventos es bastante simple con soporte de promesas en jquery. Uso lo siguiente para encadenar:
$.ajax()
.then(function(){
return $.ajax() //second ajax call
})
.then(function(){
return $.ajax() //third ajax call
})
.done(function(resp){
//handle final response here
})
Es simple, sin complicados bucles o múltiples devoluciones de llamada anidadas.
Es mucho más simple que eso.
$.ajax
ya devuelve una promesa (objeto diferido), por lo que simplemente puede escribir
function first() {
return $.ajax(...);
}
La mejor manera de hacerlo es haciendo una función reutilizable para esto. Esto incluso se puede hacer con solo una línea de código usando reduce
:
function chainPromises(list) {
return list.reduce((chain, func) => chain ? chain.then(func) : func(), null);
}
Esta función acepta una matriz de devoluciones de llamada que devuelve un objeto de promesa, como sus tres funciones.
Ejemplo de uso:
chainPromises([first, second, third]).then(function (result) {
console.log(''All done! '', result);
});
De esta forma, el resultado de first
también será automáticamente el parámetro de second
, así que básicamente lo que sucede es esto:
first().then(function(res1) { return second(res1) })
.then(function(res2) { return third(res2) })
.then(function(result) { console.log(''All done! '', result) });
Y, por supuesto, puede agregar tantas funciones a la matriz como desee.
Lo siguiente parece funcionar y permite que la lista de funciones sea dinámica:
<html>
<head>
<title>demo chained synchronous calls</title>
</head>
<body>
<script src="http://code.jquery.com/jquery-2.2.4.min.js"></script>
<script type="text/javascript">
function one(parms) {
console.log(''func one '' + parms);
return 1;
}
function two(parms) {
console.log(''func two '' + parms);
return 2;
}
function three(parms) {
console.log(''func three '' + parms);
return 3;
}
function four(parms) {
console.log(''func four '' + parms);
return 4;
}
var funcs = [''one'', ''two'', ''three'', ''four''];
var rvals = [0];
function call_next_func() {
if (funcs.length == 0) {
console.log(''done'');
} else {
var funcname = funcs.shift();
console.log(funcname);
rvals.push(window[funcname](rvals));
call_next_func();
}
}
$(document).ready(function($){
call_next_func();
});
</script>
</body>
</html>
Para encadenar jquery ajax llamé:
function A(){
return $.ajax({
url: url,
type: type,
data: data,
datatype: datatype,
success: function(data)
{
code here
}
});
}
function B(){
return $.ajax({
url: url,
type: type,
data: data,
datatype: datatype,
success: function(data)
{
code here
}
});
}
function C(){
return $.ajax({
url: url,
type: type,
data: data,
datatype: datatype,
success: function(data)
{
code here
}
});
}
A().done(function(data){
B().done(function(data){
C();
})
});
Puedes escribirlo de una manera más funcional:
[function() { return ajax(...)}, function(data) { return ajax(...)}]
.reduce(function(chain, callback) {
if(chain) {
return chain.then(function(data) { return callback(data); });
} else {
return callback();
}
}, null)
Solo tuve el mismo problema, encadenando llamadas ajax. Después de unos días de intentarlo, finalmente hice $.ajax({ async: false,...
qué hizo completamente lo que quería archivar. Simplemente no pensé en eso. Podría ayudar a otros ...