Estoy trabajando en la recuperación de datos de una API XML. Para mostrar un resultado en la pantalla, tengo que llamar a un punto final para obtener una lista de datos, luego tengo que mapear la lista de elementos y llamar a un segundo punto final para cada elemento devuelto. La primera solicitud se realiza correctamente; sin embargo, el resto de las solicitudes parecen llamarse al mismo tiempo. Esto lleva a que aproximadamente el 90% de las solicitudes regresen con el siguiente error:
Maximum allowed concurrent requests threshold(10) was breachedMi función de back-end que llama al punto final de la API toma XML como una cadena y convierte la respuesta a JSON. Aquí está el código:
export function newSoapRequest(options = { method: "POST", url: "", headers: {}, xml: "", timeout: 10000 }) { const { method, url, headers, xml, // soap envelope as string timeout } = options return new Promise((resolve, reject) => { axios({ method: method || "POST", url, headers, data: xml, timeout }).then((res) => { resolve({ res: { headers: res.headers, body: XML.parse(res.data), statusCode: res.status, } }) }).catch((err) => { if(err.response) { reject(err.response.data) } else { reject(err) } }) }) }Aquí está el código donde estoy llamando a la API desde la función de mapa
resortList.map(async (resort) => { let resortXML = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dae="DAE"><soapenv:Header/><soapenv:Body><dae:DAEGetResortProfile><!--Optional:--><dae:AuthID>GWAYUAT</dae:AuthID><!--Optional:--><dae:EndpointID>${resort.WeekEndpointID}</dae:EndpointID><!--Optional:--><dae:ResortID>${resort.ResortId}</dae:ResortID></dae:DAEGetResortProfile></soapenv:Body></soapenv:Envelope>` await newSoapRequest({method: "POST", url, headers: sampleHeaders, xml: resortXML, timeout: 10000}) .then(response => console.log(response)) .catch(err => console.log(err)) })Por lo que sé, la respuesta se envía desde la función newSoapRequest y luego se cierra la conexión. Cuando uso async await dentro de la función de mapa, la función de mapa debe esperar hasta que se complete la solicitud y la conexión se cierre antes de procesar la respuesta y luego ejecutarla por segunda vez. ¿Podría este problema estar relacionado con la función de mapa en sí misma? ¿O mi función newSoapRequest no cierra la conexión correctamente?
La función Array.prototype.map NO espera hasta que finaliza una función asíncrona. Esa es la razón por la que todos los elementos se llaman al mismo tiempo.
Para evitar eso, puede usar este enfoque:
const numbers = Array.from({ length: 10 }).map((_, i) => i + 1); const callback = async n => await new Promise(r => setTimeout(() => r(`${n} * 2 = ${n * 2}`), 10)); async function sequentialAsyncMap(arr, predicate) { const result = []; for (const item of arr) { result.push(await predicate(item)); } return result; } sequentialAsyncMap(numbers, callback) .then(console.log);Ejemplo con el paquete callback-chunk (Descargo de responsabilidad: soy el autor de este paquete):
const chunk = require('callback-chunk'); const callbacks = resortList.map(item => () => newSoapRequest({ method: 'POST', url, timeout: 10000, headers: sampleHeaders, xml: `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dae="DAE"><soapenv:Header/><soapenv:Body><dae:DAEGetResortProfile><!--Optional:--><dae:AuthID>GWAYUAT</dae:AuthID><!--Optional:--><dae:EndpointID>${item.WeekEndpointID}</dae:EndpointID><!--Optional:--><dae:ResortID>${item.ResortId}</dae:ResortID></dae:DAEGetResortProfile></soapenv:Body></soapenv:Envelope>` })); const items = await chunk(callbacks, 10, console.log);