0

I've read this post but my problem is not that.I was writing my functions needed for mongoDB GridFS management. I faced a very strange behaviour which I'm going to show you a simplified example of that. As you know, we use the callback function a lot. In my simplified example, everything works fine. The thing is that I want to update a local variable with the value that comes from the callback function:

function secondFunction(input, callbackFunction) {
    var result = input + 5;
    callbackFunction(result);
}

function thirdFunction(input, callbackFunction) {
    var result = input + 5;
    callbackFunction(result);
}

function mainFunction(input) {
    var Answer = 0;
    secondFunction(input, function (result) {
        thirdFunction(result, function (result) {
            Answer = result;
        })
    });
    console.log(Answer);
}

mainFunction(5);

The result as you expect is 15. The Answer global variable has been updated successfully. But my problem is the code below which seems that the local variable has not been changed. In this example I wanted to get my images one by one from the gridFS and push them into an array called images (which is the local variable):

function getAll(callback) {
    var images = [];
    db.collection('fs.files').find({}).toArray(function (err, files) {
        var Files = files;
        for (var i = 0; i < Files.length; i++) {
            getFileById(Files[i]._id, function (err, img) {
                if (err) {
                    callback(err, null);
                }
                else images.push(img);
            })
        }
    });
    console.log(images);
}

The result is an [] array. What mistake am I making?

Edit:

function getFileById(id, callback) {
gfs.createReadStream({_id: id},
    function (err, readStream) {
        if (err) callback(err, null);
        else {
            if (readStream) {
                var data = [];
                readStream.on('data', function (chunk) {
                    data.push(chunk);
                });
                readStream.on('end', function () {
                    data = Buffer.concat(data);
                    var img = 'data:image/png;base64,' + Buffer(data).toString('base64');

                    setTimeout(function () {
                        callback(null, img);
                    }, 2000);

                })
            }
        }
    });
}

function getAll(callback) {
    var images = [];
    db.collection('fs.files').find({}).toArray(function (err, files) {
        var Files = files;
        for (var i = 0; i < Files.length; i++) {
            getFileById(Files[i]._id, function (err, img) {
                if (err) {
                    callback(err, null);
                }
                else images.push(img);
            })
        }
    });
    console.log(images);
}
Kasra GH
  • 157
  • 2
  • 5
  • 22
  • The basic mistake is `console.log()` gets executed before the callback to the async function is run. Nothing here tells it to wait. The two reference questions show you how you deal with results "inside the callback" or otherwise deal with "awaiting" the async response. – Neil Lunn Aug 28 '17 at 11:47
  • But why in my simplified example everything goes smoothly? I don't understand the difference between my simplified example and my real problem. – Kasra GH Aug 28 '17 at 11:56
  • Because you are just passing values. Nothing actually needs to "wait" for I/O. All the "why" is actually already explained when you take the time to read the existing material. – Neil Lunn Aug 28 '17 at 11:59
  • I read the post you mentioned but my problem is not that. When I log the array inside the **for** loop everything works fine. – Kasra GH Aug 28 '17 at 12:14
  • You can't be reading very carefully then. This is a pretty basic principle of async calls. If you did not read such an example already, then wrap your own inner function examples with [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout), which will actually simulate I/O waiting. Then you might just understand. – Neil Lunn Aug 28 '17 at 12:22
  • My inner function has been already written in that way. See the update. Is it what you meant? – Kasra GH Aug 28 '17 at 12:29

1 Answers1

0

What if you put the console.log("inside") inside forloop? I think your console.log(images)

renders before the loops begins

Granit
  • 188
  • 1
  • 11
  • I know. As you said, it only works when I put it inside the for loop. But I don't understand what is the difference between my simplified example and my real problem. They look the same – Kasra GH Aug 28 '17 at 11:54
  • Is this nodejs? I think you can read more here: http://exploringjs.com/es6/ch_promises.html – Granit Aug 28 '17 at 11:59
  • yes this is nodeJS. I've updated my code to show you more detail. What is the problem? – Kasra GH Aug 28 '17 at 12:30