Sebastián Ardila
Sebastián Ardila

Reputation: 137

Callback executed before function finishes execution

Hello I have this problem, whenever I execute this code the callback is executed before de main function finishes the execution. I believe it has something to do with async functions

var leers3 = async function (next) {
    var executed = [];
    AWS.config.update({
        region: '********',
        accessKeyId: '*********',
        secretAccessKey: '*********'
    });

    var s3 = new AWS.S3();

    s3.listObjects({Bucket:'*******'}, function(err, data) {
        if (err) {
            console.log(fechaActual() + " Error: Error ejecutando cruce con S3.")
        }else{
            var files = [];
            data.Contents.forEach(function(obj,index){
                if(obj.Key.startsWith("*****") || obj.Key.startsWith("*****")){
                    files.push(obj.Key);
                }
            })
            files.forEach((file) => {
                var id_cajero = file.split('_')[1];
                var params = {Bucket:'*****', Key: file};
                var sql = "SELECT id_entidad FROM cajero WHERE id_cajero = '" + id_cajero + "';";
                dbConnection.query(sql, async(err,result) => {
                    if(err) console.log(err);
                    else{
                        var fileExecutedData = await leerarchivos3(params, s3, id_cajero, result[0].id_entidad);
                        fileExecutedData.file = file;
                        executed.push(fileExecutedData);
                        //console.log(executed);    
                    }
                })
                next(executed);                 
            })
        }
    })
};

leers3((executed) => {
        console.log(executed);
    });

Upvotes: 1

Views: 255

Answers (2)

Dan Starns
Dan Starns

Reputation: 3823

forEach is not designed to handle/execute promises & async code. forEach is designed to be a synchronous operation. This means that for each file you iterate over, where you think the execution is pausing, in fact, Javascript continues to the next iteration. You won't be able to guarantee the execution time of the dbConnection.query callbacks. If you use Promise.all with a little change up to Array.map you can achieve a more predictable outcome & leverage the 'parallel' capabilities Promise.all gives. Here is pseudo-code inspired by your example.

    await Promise.all(
        files.map((file) => {
            const id_cajero = file.split("_")[1];
            const params = { Bucket: "*****", Key: file };
            const sql = `SELECT id_entidad FROM cajero WHERE id_cajero = '${id_cajero}';`;

            return new Promise((resolve, reject) => {
                dbConnection.query(sql, async (err, result) => {
                    if (err) {
                        return reject(err);
                    }

                    const fileExecutedData = await leerarchivos3(
                        params,
                        s3,
                        id_cajero,
                        result[0].id_entidad
                    );

                    fileExecutedData.file = file;

                    executed.push(fileExecutedData);

                    return resolve();
                });
            });
        })
    );

    next(executed);

Upvotes: 2

Jason Wadsworth
Jason Wadsworth

Reputation: 8887

I would recommend turning your s3.listObjects call into a promise.

const data = await s3.listObjects({Bucket:'*******'}).promise();
// ... do things with the data that is currently in your callback

I assume the dbConnection.query has an async/await (i.e. Promise) option as well.

If you must use callbacks then you should remove the async from the function.

Upvotes: 0

Related Questions