ekkis ekkis - 4 months ago 8
Node.js Question

How do I wait for a pull?

I need to run a container from node so I'm using the

dockerode
module, which uses the Docker API. To run a container I first need to pull the image (if it doesn't exist locally). I've promisified the calls for simplicity but I'm having difficulty figuring out how to connect the calls, particularly with respect to the callback that
pull
offers. here's what I have:

init: function(opts) {
var self = this;
self.docker = new Docker();
self.pull = Bluebird.promisify(
self.docker.pull, { context: self.docker }
);
self.createContainer = Bluebird.promisify(
self.docker.createContainer, { context: self.docker }
);
},

run: function(opts) {
var self = this;
return self.createContainer(opts).catch(function(e) {
return self.pull(opts.Image).then(function(stream) {
return self.createContainer(opts);
});
}).then(function(o) {;
var start = Bluebird.promisify(
o.start, { context: o }
);
return start({});
}).then(function(o) {;
var inspect = Bluebird.promisify(
o.inspect, {context: o}
);
return inspect();
});
},


so what happens is the first
createContainer
fails (because the image hasn't been loaded yet) and a call to
pull
happens. when that succeeds,
createContainer
is called.

at present that call is failing because the image didn't yet get time to load. I see in the docs: https://github.com/apocas/dockerode (see Helper Functions) that I can do:

return self.pull(opts.Image).then(function(stream) {
self.docker.modem.followProgress(stream, function(err, output) {
return self.createContainer(opts);
});
});


...which I think means
createContainer
will only fire when the pull has finished, but the problem is I've broken the promises. what's the correct way of dealing with this?

TIA - e

Answer

Speaking in generalities here, suppose you have:

return createsPromiseA().then((a) => {
  takesNodebackB(a, (err, b) => {
    let promiseC = createsPromiseC();
    // How do I return promiseC??
  });
});

There are 2 answers. One uses what comes with native promises ootb:

return createsPromiseA().then((a) => {
  return new Promise((resolve, reject) => {
    takesNodebackB(a, (err, b) => {
      if (err) return reject(err);
      resolve(b);
    });
  });
}).then((b) => createsPromiseC());

The trick here is wrapping the nodeback function in a promise.

The second answer is that bluebird ships with http://bluebirdjs.com/docs/api/promise.promisify.html which, while I haven't used it, should allow you to do the same thing, something like:

return createsPromiseA()
  .then((a) => bluebird.promisify(takesNodebackB)(a))
  .then((b) => createsPromiseC());
Comments