Así es como estoy usando un bucle forEach secuencial para manejar algunas cargas de archivos. Ahora quiero obtener un resultado total para cada carga: para cada archivo, resuelvo con un objeto (éxito o error). Al final, quiero obtener una matriz devuelta para mostrar el resultado (Archivo 1 exitoso, error de archivo 2, etc.).
Pero con este código solo obtengo undefined para la última salida. ¿Qué me estoy perdiendo?
Tal vez incluso sería mejor devolver dos matrices de cadenas: una matriz exitosa y otra fallida con nombres de archivo.
Array.prototype.forEachSequential = async function ( func: (item: any) => Promise<void> ): Promise<void> { for (let item of this) await func(item) } async uploadFiles(files: [File]): Promise<any> { const result = await files.forEachSequential( async (file): Promise<any> => { const res = await new Promise(async (resolve, reject) => { // do some stuff resolve({ success: file.filename }) // or maybe resolve({ error: file.filename }) }) console.log(res) // returns object as expected return res } ) // Get all results after running forEachSequential console.log('result', result) // result: undefined })Su función forEachSequential no tiene una declaración de return , por lo que nunca devuelve nada, por lo que llamarla siempre dará como resultado undefined (en este caso específico, una promesa que se cumple con undefined , ya que es una función async ). Ignora por completo el valor de retorno de la devolución de llamada.
Probablemente desee un mapSequential (o mapSerial ) en su lugar, que genera una matriz de los resultados:
Object.defineProperty(Array.prototype, "mapSequential", { async value(func) { const result = []; for (const item of this) { result.push(await func(item)); } return result; }, writable: true, configurable: true, }); Nota: le recomiendo que no agregue a Array.prototype . Considere hacer esto en su lugar:
async function mapSequential(iterable, func) { const result = []; for (const item of iterable) { result.push(await func(item)); } return result; } Pero si agrega a Array.prototype , asegúrese de hacerlo con defineProperty como lo hice anteriormente, no solo con la asignación, y asegúrese de que la propiedad que agrega no sea enumerable (use enumerable: false o déjelo fuera, false es el predeterminado).
Puedes girar esto de muchas maneras. Podría considerar una variante similar a Promise.allSettled que detecta errores y devuelve una matriz que indica éxito/fracaso (con o sin detenerse antes del primer error). Por ejemplo:
async function mapSequential(iterable, func, options) { const result = []; for (const item of iterable) { try { const value = await func(item); result.push({ status: "fulfilled", value }); } catch (reason) { result.push({ success: "rejected", reason }); if (options?.shortCircuit) { // Option that says "stop at first error" break; } } } return result; } De nuevo, sin embargo, puedes girarlo de muchas maneras. Tal vez agregue un índice y el iterable a la devolución de llamada como lo hace el map , tal vez tenga un thisArg opcional (aunque las funciones de flecha lo hacen en gran medida innecesario en estos días), etc.
Su forEachSequential realmente no devuelve nada. Es por eso que obtienes undefined . Cuando esté esperando, debe adjuntar un resultado a alguna variable, almacenarlo en algún lugar y luego, al final de esta función, debe devolverlo.
Ahora, le gustaría devolver una tupla [string[], string[]] . Para hacerlo, debe crear matrices al comienzo del cuerpo forEachSequential , luego llamar a func en el ciclo y agregar un resultado a la primera matriz (digamos, matriz exitosa) o segunda (matriz fallida).
const successArray = []; const failureArray = []; for (let item of this) { const result = await func(item); if ('success' in result) { successArray.push(result.success); } else { failureArray.push(result.error); } } return [successArray, failureArray];