0

I have come across similar questions, but none that quite fits my scenario.

In the code below, I am using the listContainers() function from the dockerode Javascript library, to list the ids of my Docker containers. The snippet is adapted from one on the dockerode README. The listContainers call works and the console.log line outputs the ids as expected. The problem is that I cannot push the container IDs into the array declared outside the function call - the result is that the array is still empty after the listContainers call.

I am not very experienced with Javascript, but I think this problem is due to attempting the push inside the callback function. The problem is that the listContainers is asynchronous, so this means that //CONSOLE LOG #2 actually executes before //CONSOLE LOG #1.

How can I capture the id values into the ids array outside the function call?

//Here is where I want to store my container ids.
var ids = [];

//Here is the dockerode call
dockerCli.listContainers(function(err, containers) {

    containers.forEach(function(containerInfo) {

        //log shows the correct id
        console.log(containerInfo.Id);

        //Here I try to save the container id to my array
        ids.push(containerInfo.Id);
    });

    //CONSOLE LOG #1 Here I can see that the array has the correct values
    console.log("IDs: "+ids.toString());
});

//CONSOLE LOG #2 Shows that the array is empty
console.log("IDs: "+ids.toString());
EkcenierK
  • 1,429
  • 1
  • 19
  • 34
  • 1
    Does `listContainers` result in an asynchronous callback? – James Thorpe Nov 19 '15 at 16:17
  • Hmm, it makes an external call to a Docker server, so yes I think so. Do I need to use a Javascript promise? – EkcenierK Nov 19 '15 at 16:19
  • 1
    That may be one way to work with it, yes. See [this question](http://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call) - the way the question is phrased is slightly different (in that it's trying to return from a function rather than an inline callback) but the premise is the same. – James Thorpe Nov 19 '15 at 16:20

2 Answers2

1

With the help of other commenters, I realised that the listContainers call is asynchronous and there is simply no way to return a value from it.

So how to initialise and work with my ids array? I created my own function that wraps the dockerode listContainers call. This function then takes its own callback to do the work with the ids array. This allowed me to access the initialised ids array in my own callback, separating the functionality of processing the ids array from fetching the container list.

ids = [];

//Define my function that takes a callback function
//and just fetch the container ids
function getContainerJsonFromDocker(callback) {

    dockerCli.listContainers(function(err, containers) {

        containers.forEach(function(containerInfo) {
            console.log(containerInfo.Id);
            ids.push(containerInfo.Id);
        });
        return callback(ids);
    });
}

//Now call my function, and pass it an anonymous callback
//The callback does the processing of the ids array
getContainerJsonFromDocker(function(ids) {

    //This shows the array is initialised :)
    console.log("IDs: " + ids.toString());

    //Write my array to .json file
    var outputFilename = 'data.json';
    fs.writeFile(outputFilename, JSON.stringify(ids, null, 4),
            function(err) {
                if (err) {
                    console.log(err);
                } else {
                    console.log("JSON saved to " + outputFilename);
                }
            });
});
EkcenierK
  • 1,429
  • 1
  • 19
  • 34
0

you dont need to pass the global variable ids along with callback try this,

//Here is where I want to store my container ids.
var ids = [];

//Here is the dockerode call
dockerCli.listContainers(function(err, containers) {

    containers.forEach(function(containerInfo) {

        //log shows the correct id
        console.log(containerInfo.Id);

        //Here I try to save the container id to my array
        ids.push(containerInfo.Id);
    });
});

//Shows that the array is empty
console.log("IDs: "+ids.toString());
Azad
  • 5,144
  • 4
  • 28
  • 56
  • The result is still the same if I do not pass the variable. The problem is something else, to do with scope inside the callbacks – EkcenierK Nov 19 '15 at 16:38
  • have you tried your console out put inside the `listContainers` callback function? – Azad Nov 19 '15 at 16:39
  • Yes, `console.log("IDs: "+ids.toString());` works if I place it outside the `forEach`, but inside the `listContainers` function. The problem is that the `listContainers`is asynchronous, so this does not get called until after my other code has executed. I will update my question. – EkcenierK Nov 19 '15 at 16:43
  • how do you calling this callback function? – Azad Nov 19 '15 at 16:44
  • The callback functions are the anonymous functions passed to `forEach` and `listContainers` - see the function calls in the code I posted. `listContainers` is part of an external library, dockerode. – EkcenierK Nov 19 '15 at 16:49
  • 1
    Its clear, that ids only filled up when this callback is executed – Azad Nov 19 '15 at 16:52
  • Exactly. Do you have a suggestion of how to capture this after the callback? – EkcenierK Nov 19 '15 at 16:56
  • return the ids after the foreach – Azad Nov 19 '15 at 16:58
  • That doesn't work, because still the asynchronous listContainers call doesn't return the ids value until after it completes, so the value is undefined outside it. – EkcenierK Nov 19 '15 at 17:06
  • 1
    @KLibby - What kind of suggestion are you looking for? You cannot output data that has not arrived yet. Have you read the question at the link provided by @JamesThorpe? – Igor Nov 19 '15 at 17:16
  • @Igor Yes, thanks, this answer is helping along with this discussion. I understand now I cannot capture the output immediately from the asynchronous code. I am working on an alternative solution, but I will accept any suggestions of a different way to implement it. I will post my solution when I have it. – EkcenierK Nov 19 '15 at 17:26