0

In the code below, when I run in debug mode with a break-point at this line: content.push(data.Body.toString()); I can see that data is inserted to the content array.

However when I run the code normally, content comes back empty.

How can I get it to populate the array for downstream use?

var params = { Bucket: "thebucket", Prefix: "theprefix/" }
var content = [];
function getS3Data()
{    
var s3 = new aws.S3();
s3.listObjects(params, function (err, data) 
{        
    if (err) throw err; // an error occurred
    else 
    {            
        var i;
        for (i = 0; i < data.Contents.length; i++)              
        {
            var currentValue =  data.Contents[i];
            if(currentValue.Key.endsWith(params.Prefix) == false) 
            {
                var goParams = { Bucket: params.Bucket, Key: currentValue.Key };                   
                s3.getObject(goParams, function(err, data) 
                {                        
                    if (err) throw err; //error                        
                    content.push(data.Body.toString());       
                });
            };
        };            
    }//else
});//listObjects
}//getS3Data

getS3Data();

console.log(content); //prints empty here when run in non-debug.
Kenobi
  • 465
  • 6
  • 13

2 Answers2

0

Node.js' documentation has an example very similar to the problem you are experiencing:

The issue arises because the variable content is not set as soon as getS3Data has finished, because it is an asynchronous function. content will be set some time later. But your call to console.log(content); will execute immediately after getS3Data has finished, so at that point content has not been set yet.

You can test that by adding an extra log:

s3.getObject(goParams, function(err, data) 
{                        
    if (err) throw err; //error                        
    content.push(data.Body.toString());
    console.log("Content has been assigned");
});

And then change the bottom to:

getS3Data();
console.log("getS3Data has finished", content);

It's likely you'll get the messages in this order:

getS3Data has finished
Content has been assigned
Daniel
  • 21,933
  • 14
  • 72
  • 101
0

The line:
console.log(content)
is being executed before the line:
content.push(data.Body.toString());

the function you are passing as a 2nd argument to s3.listObjects will be executed asynchronously. If you want to log out content you need to do it within the callback function meaning:

s3.listObjects(params, function (err, data) {        
    if (err) throw err;
    else {            
        // ...
        console.log(content)
    }
});

A better approach would be to implement getS3Data with Promise so you can run code after the object listing is done for sure.

function getS3Data() {
    return new Promise((resolve, reject) => {
        if (err) {
            reject(err)
        } else {
            const promises = []
            for (const i = 0; i < data.Contents.length; i++) {
                const currentValue = data.Contents[i];
                if (currentValue.Key.endsWith(params.Prefix) == false) {
                    const goParams = { Bucket: params.Bucket, Key: currentValue.Key };
                    promises.push(new Promise((res, rej) => {
                        s3.getObject(goParams, function (err, data) {
                            if (err) {
                                rej(err); //error
                            } else {
                                res(data.Body.toString());
                            }                        
                        });
                    }));
                }
            }
            Promise.all(promises).then(resolve);
        }
    });
}

getS3Data()
    .then(result => { // this will actually be `content` from your code example
        console.log(result);
    }).catch(error => {
        console.error(error);
    })
Ramy Ben Aroya
  • 2,333
  • 14
  • 20