简体   繁体   中英

How to return value in sequential forEach loop?

This is how I'm using a sequential forEach loop to handle some file uploads. Now I want to get a total result for each upload: For each file I resolve with an object (success or error). At the end I want to get a returned array to display the result (File 1 successful, file 2 error and so on).

But with this code I only get undefined for the last output. What am I missing?

Maybe it would even be better to return two string arrays: one successful and one failed array with filenames.

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
})

Your forEachSequential function has no return statement, so it never returns anything, so calling it will always result in undefined (in this specific case, a promise that's fulfilled with undefined , since it's an async function). It completely ignores the return value of the callback.

You probably want a mapSequential (or mapSerial ) instead, that builds an array of the results:

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,
});

Note: I recommend you don't add to Array.prototype . Consider doing this instead:

async function mapSequential(iterable, func) {
    const result = [];
    for (const item of iterable) {
        result.push(await func(item));
    }
    return result;
}

But if you do add to Array.prototype , be sure to do it with defineProperty as I did above, not just with assignment, and make sure the property you add isn't enumerable (either use enumerable: false or leave it off, false is the default).

You can spin this a lot of ways. You might consider a variant similar to Promise.allSettled that catches errors and returns an array indicating success/failure (with or without stopping early on first error). For instance:

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;
}

Again, though, you can spin it lots of ways. Maybe add an index and the iterable to the callback like map does, perhaps have an optional thisArg (although arrow functions make that largely unnecessary these days), etc.

Your forEachSequential doesn't really return anything. That's why you get undefined . When awaiting, you should attach a result to some variable, store it somewhere and then at the end of this function you should return it.

Now, you would like to return a tuple [string[], string[]] . To do so you have to create to arrays at the beginning of forEachSequential body, then call func in the loop and add a result to first array (let's say - success array) or second (failure array).

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];

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM