Tengo un fragmento de código que ejecuta una larga lista de solicitudes http y he escrito el código de tal manera que siempre tiene 4 solicitudes ejecutándose en paralelo. Da la casualidad de que el servidor puede manejar 4 solicitudes paralelas más rápido. Con menos, el código funcionaría más lento y con más, las solicitudes tardarían más en finalizar. De todos modos, aquí está el código:
const itemsToRemove = items.filter( // ... ) const removeItem = (item: Item) => item && // first it checks if item isn't undefined // Then it creates a DELETE request itemsApi.remove(item).then( // And then whenever a request finishes, // it adds the next request to the queue. // This ensures that there will always // be 4 requests running parallel. () => removeItem(itemsToRemove.shift()) ) // I start with a chunk of the first 4 items. const firstChunk = itemsToRemove.splice(0, 4) await Promise.allSettled( firstChunk.map(removeItem) )Ahora, el problema con este código es que si la lista es muy larga (como en miles de elementos), en algún momento la pestaña del navegador simplemente falla. Lo cual es un poco inútil, porque no puedo ver un mensaje de error específico que me diga qué salió mal.
Pero supongo que esta parte del código:
itemsApi.remove(item).then( () => removeItem(itemsToRemove.shift()) ) ¿Puede estar creando un problema de Maximum call stack size exceeded ? Porque de alguna manera estoy constantemente agregando a la pila de llamadas, ¿no es así?
¿Crees que mi conjetura es correcta? E independientemente de si su respuesta es sí o no, ¿tiene alguna idea de cómo podría lograr el mismo objetivo sin bloquear la pestaña del navegador? ¿Puedo refactorizar este código de manera que no se agregue a la pila de llamadas? (¿Si de hecho estoy haciendo eso?)
El problema con su código está en
await Promise.allSettled(firstChunk.map(removeItem) El argumento pasado a Promise.allSettled debe ser una matriz de Promises según la documentación :
El método Promise.allSettled() devuelve una promesa que se cumple después de que todas las promesas dadas se hayan cumplido o rechazado, con una matriz de objetos que describen el resultado de cada promesa.
Su función recursiva luego ejecuta todas las solicitudes una tras otra, arrojando un error de Maximum call stack size exceeded y bloqueando su navegador.
La solución que se me ocurrió (probablemente podría acortarse) es así:
let items = [] while (items.length < 20) { items = [...items, `item-${items.length + 1}`] } // A mockup of the API function that executes an asynchronous task and returns once it is resolved async function itemsApi(item) { await new Promise((resolve) => setTimeout(() => {resolve(item)}, 1000)) } async function f(items) { const itemsToRemove = items // call the itemsApi and resolve the promise after the itemsApi function finishes const removeItem = (item) => item && new Promise((resolve, reject) => itemsApi(item) .then((res) => resolve(res)) .catch(e => reject(e)) ) // Recursive function that removes a chunk of 4 items after the previous chunk has been removed function removeChunk(chunk) { // exit the function once there is no more items in the array if (itemsToRemove.length === 0) return console.log(itemsToRemove) // after the first 4 request finish, keep making a new chunk of 4 requests until the itemsToRemove array is empty Promise.allSettled(chunk.map(removeItem)) .then(() => removeChunk(itemsToRemove.splice(0, 4))) } const firstChunk = itemsToRemove.splice(0, 4) // initiate the recursive function removeChunk(firstChunk) } f(items)Espero que esto responda tu pregunta