javascript - promises - use forEach() con promesas, mientras que el acceso anterior promete resultados en una cadena.then()?
promises javascript w3schools (4)
Tengo las siguientes funciones con promesas:
const ajaxRequest = (url) => {
return new Promise(function(resolve, reject) {
axios.get(url)
.then((response) => {
//console.log(response);
resolve(response);
})
.catch((error) => {
//console.log(error);
reject();
});
});
}
const xmlParser = (xml) => {
let { data } = xml;
return new Promise(function(resolve, reject) {
let parser = new DOMParser();
let xmlDoc = parser.parseFromString(data,"text/xml");
if (xmlDoc.getElementsByTagName("AdTitle").length > 0) {
let string = xmlDoc.getElementsByTagName("AdTitle")[0].childNodes[0].nodeValue;
resolve(string);
} else {
reject();
}
});
}
Estoy tratando de aplicar esas funciones para cada objeto en el conjunto de JSON:
const array = [{"id": 1, "url": "www.link1.com"}, {"id": 1, "url": "www.link2.com"}]
Se me ocurrió la siguiente solución:
function example() {
_.forEach(array, function(value) {
ajaxRequest(value.url)
.then(response => {
xmlParser(response)
.catch(err => {
console.log(err);
});
});
}
}
Me preguntaba si esta solución es aceptable con respecto a 2 cosas:
¿Es una buena práctica solicitar forEach () sobre promesas en el siguiente asunto?
¿Hay alguna forma mejor de pasar los resultados prometedores anteriores como parámetro en la cadena then ()? (Estoy pasando el param de
response
).
¿Hay alguna forma mejor de pasar los resultados prometidos anteriores como parámetro en la cadena then ()?
De hecho, puede encadenar y resolver promesas en cualquier orden y en cualquier lugar de código. Una regla general: cualquier promesa encadenada con la rama de catch
o catch
es solo una nueva promesa, que debería encadenarse más tarde.
Pero no hay limitaciones. Con el uso de bucles, la solución más común es reduce
doblez del lado izquierdo, pero también puede usar la variable simple let
con reasignación con nuevas promesas.
Por ejemplo, incluso puede diseñar cadena de promesas diferidas:
function delayedChain() {
let resolver = null
let flow = new Promise(resolve => (resolver = resolve));
for(let i=0; i<100500; i++) {
flow = flow.then(() => {
// some loop action
})
}
return () => {
resolver();
return flow;
}
}
(delayedChain())().then((result) => {
console.log(result)
})
El único problema con el código que proporcionó es que el resultado de xmlParser se pierde, ya que cada ciclo solo itera pero no almacena los resultados. Para mantener los resultados, necesitará utilizar Array.map, que obtendrá Promise como resultado, y luego Promise.all para esperar y obtener todos los resultados en una matriz.
Sugiero usar async / await de ES2017 que simplifica el manejo de las promesas. Dado que el código proporcionado ya utiliza las funciones de flecha, lo que requeriría transpiling para la compatibilidad de navegadores más antiguos, puede agregar el complemento de transpiling para admitir ES2017.
En este caso, su código sería como:
function example() {
return Promise.all([
array.map(async (value) => {
try {
const response = await ajaxRequest(value.url);
return xmlParser(response);
} catch(err) {
console.error(err);
}
})
])
}
El código anterior ejecutará todas las solicitudes en paralelo y devolverá los resultados cuando finalicen todas las solicitudes. También es posible que desee disparar y procesar las solicitudes una por una, esto también proporcionará acceso al resultado de la promesa anterior si esa fuera su pregunta:
async function example(processResult) {
for(value of array) {
let result;
try {
// here result has value from previous parsed ajaxRequest.
const response = await ajaxRequest(value.url);
result = await xmlParser(response);
await processResult(result);
} catch(err) {
console.error(err);
}
}
}
Otra solución es utilizar Promise.all
para hacer esto, creo que es una mejor solución que hacer un bucle alrededor de las solicitudes de Ajax.
const array = [{"id": 1, "url": "www.link1.com"}, {"id": 1, "url": "www.link2.com"}]
function example() {
return Promise.all(array.map(x => ajaxRequest(x.url)))
.then(results => {
return Promise.all(results.map(data => xmlParser(data)));
});
}
example().then(parsed => {
console.log(parsed); // will be an array of xmlParsed elements
});
Puede usar .reduce()
para acceder a Promise
anterior.
function example() {
return array.reduce((promise, value) =>
// `prev` is either initial `Promise` value or previous `Promise` value
promise.then(prev =>
ajaxRequest(value.url).then(response => xmlParser(response))
)
, Promise.resolve())
}
// though note, no value is passed to `reject()` at `Promise` constructor calls
example().catch(err => console.log(err));
Tenga en cuenta que el constructor Promise
no es necesario en la función ajaxRequest
.
const ajaxRequest = (url) =>
axios.get(url)
.then((response) => {
//console.log(response);
return response;
})
.catch((error) => {
//console.log(error);
});