Я начинаю с javascript и пытаюсь создать чистый объект для использования библиотеки Dockerode в моем примере использования. У меня проблема с асинхронностью здесь, моя строка 27 выполняется до 24, и я не понимаю, почему и как ее исправить!
Кроме того, если вам это проще, посетите эту публикацию: https://gist.github.com/msitruk/2cdb655a0bebdb29c61d8bc5606a2695
const Docker = require('dockerode');
const docker = new Docker({
socketPath: '/var/run/docker.sock'
});
// CONSTRUCTOR
function SearchUtils() {
this.listContainersPromise = docker.listContainers({all: true});
this.scraperListId = [];
}
// "METHODS"
SearchUtils.prototype.run = function() {
this.getScraperContainersListPromise()
.then((containers) => {
for (let i = 0; i < containers.length; i++) {
if (containers[i].Names.toString().indexOf("scraper") !== -1) {
this.addToScraperList(containers[i].Id, "wait");
}
}
}, (err)=>{console.log(err)})
.then(()=>{
this.checkReadyScraper();
},(err)=>{console.log(err)})
.then(() => {
this.scrap();
}, (err)=>{console.log(err)});
};
SearchUtils.prototype.checkReadyScraper = function() {
for (let i = 0; i < this.scraperListId.length; i++) {
this.exec("getStatus", this.scraperListId[i].id);
}
};
SearchUtils.prototype.getScraperContainersListPromise = function() {
return this.listContainersPromise; // <- Not working
};
SearchUtils.prototype.exec = function(type, containerId){
let container = docker.getContainer(containerId);
if (type === "getStatus"){
this.runExec(container, 'cat /home/immobot/status');
}
else if (type === "scrap") {
this.runExec(container, 'torify scrapy crawl seloger -o seloger.json');
}
};
SearchUtils.prototype.scrap = function() {
let localRdyScraperList = [];
for (let i = 0; i < this.scraperListId.length; i++) {
if(this.scraperListId[i].status.toString('utf8').indexOf("ready") !== -1){
localRdyScraperList.push(this.scraperListId[i].id);
}
}
console.log("test de localRdyScraperList : "+localRdyScraperList);
// this.exec("scrap", this.scraperListId[i].id);
};
SearchUtils.prototype.addToScraperList = function(containerId,status) {
this.scraperListId.push({id: containerId, status: status});
};
SearchUtils.prototype.getScraperList = function() {
return this.scraperListId;
};
SearchUtils.prototype.getScraperList = function() {
return this.scraperListId;
};
SearchUtils.prototype.runExec = function (container, cmd) {
let options = {
Cmd: [ '/bin/bash', '-c', cmd ],
AttachStdout: true,
AttachStderr: true
};
container.exec(options, (err, exec) => {
if (err) return;
exec.start((err, stream) => {
if (err){
console.log("error : "+err);
return;
}
// container.modem.demuxStream(stream, process.stdout, process.stderr)
if (cmd === "cat /home/immobot/status"){
let newStream = require('stream');
let logStream = new newStream.PassThrough();
logStream.on('data', (chunk) => {
// console.log(chunk.toString('utf8'));
if (chunk.toString('utf8').indexOf("ready") !== -1){
console.log("CONTAINER READY !!");
//EDIT CONTAINER STATUS IN SCRAPERLIST TO READY
this.changeStatusToReady(container.id);
}
});
container.modem.demuxStream(stream, logStream, process.stderr);
}
else if (cmd === "torify scrapy crawl seloger -o seloger.json"){
console.log("on lance le scrape sur un des scraper rdy");
container.modem.demuxStream(stream, process.stdout, process.stderr)
}
// container.modem.demuxStream(stream, logStream, process.stderr);
exec.inspect(function(err, data) {
if (err){
console.log("error : "+err);
return;
}
});
});
});
};
SearchUtils.prototype.changeStatusToReady = function (containerId){
for (let i = 0; i < this.scraperListId.length; i++) {
if(this.scraperListId[i].id === containerId){
this.scraperListId[i].status = "ready";
}
}
// console.log(this.getScraperList());
};
module.exports = SearchUtils;
Если ваша цепочка обещает, не забудьте вернуться к следующему обещанию.
например..
.then(()=>{
this.checkReadyScraper();
}
Если checkReadyScraper()
является обещанием, тогда вы захотите его вернуть.
например.
.then(()=>{
return this.checkReadyScraper();
}
В противном случае все ваши действия выполняются checkReadyScraper()
и полностью игнорируют возвращенное обещание.
Вот как я думаю, что ваш runExec должен выглядеть. Я предполагаю, что exec.inspect
- это то, что вы хотите разрешить.
SearchUtils.prototype.runExec = function (container, cmd) {
return new Promise ((resolve, reject)=>{
let options = {
Cmd: [ '/bin/bash', '-c', cmd ],
AttachStdout: true,
AttachStderr: true
};
container.exec(options, (err, exec) => {
if (err) return reject(err); //return error
exec.start((err, stream) => {
if (err){
console.log("error : "+err);
return reject(err); //return error
}
// container.modem.demuxStream(stream, process.stdout, process.stderr)
if (cmd === "cat /home/immobot/status"){
let newStream = require('stream');
let logStream = new newStream.PassThrough();
logStream.on('data', (chunk) => {
// console.log(chunk.toString('utf8'));
if (chunk.toString('utf8').indexOf("ready") !== -1){
console.log("CONTAINER READY !!");
//EDIT CONTAINER STATUS IN SCRAPERLIST TO READY
this.changeStatusToReady(container.id);
}
});
container.modem.demuxStream(stream, logStream, process.stderr);
}
else if (cmd === "torify scrapy crawl seloger -o seloger.json"){
console.log("on lance le scrape sur un des scraper rdy");
container.modem.demuxStream(stream, process.stdout, process.stderr)
}
// container.modem.demuxStream(stream, logStream, process.stderr);
exec.inspect(function(err, data) {
if (err){
console.log("error : "+err);
//don't forget to return the rejection
return reject(err);
}
//looks like everything was ok, lets resolve
resolve(data);
});
});
});
//resolve("ok"); too early
// TODO ADD EROR STRATEGY
//reject("error"), pointless
});
};
checkReadyScraper()
не является обещанием. Вам нужно убедиться, что все, что находится на линии ниже, возвращает обещание, глядя на ваш gitHub gist, ваш exec
является асинхронной функцией, потому что она вызывает runExec, вам нужно превратить runExec в функция, основанная на обещаниях.
Во-первых - нет необходимости обрабатывать ошибки в каждом вызове then(). Вы можете реализовать единую ошибку, которая будет ловить ошибку в любом элементе then() в последовательности:
.then(()=> {
this.checkReadyScraper();
})
.then(() => {
this.scrap();
})
.catch(e => console.log(e))
Также обратите внимание, что функция стрелок, подобная catch (e => console.log(e)), не требует {} и;
Ваша проблема в том, что ваша задача - Async. Если вы хотите связать задачи - вы должны сделать задачу, чтобы вернуть обещание
Это грубо говоря, что вы должны реорганизовать:
//Should return Promise
SearchUtils.prototype.exec = function(type, containerId){
let container = docker.getContainer(containerId);
if (type === "getStatus"){
//runExec should return us a Promise
return this.runExec(container, 'cat /home/immobot/status');
}
else if (type === "scrap") {
return this.runExec(container, 'torify scrapy crawl seloger -o seloger.json');
}
};
SearchUtils.prototype.runExec = function (container, cmd) {
return new Promise(function(resolve, reject) {
//do some stuff
//then call resolve(result)
//or call reject(error)
});
}
После этого вы сможете объединить обещания (что на самом деле довольно удивительно и помогает решать обратный ад):
.then(()=> {
//this not returns Promise, and it will be correctly chained
return this.checkReadyScraper();
})
.then(() => {
//this not returns Promise, and it will be correctly chained
return this.scrap();
})
.catch(e => console.log(e))
Кроме того, чтобы это выглядело чище, я даже рекомендую сделать небольшой рефакторинг, который, наконец, даст вам oneliner:
.then(this.checkReadyScraper).then(this.scrap).catch(console.log)
Выполнение задачи (с контейнером.exec, в строке 81 в вашем коде) выполняется асинхронно с остальными шагами, с обратным вызовом, когда они будут выполнены. Вам нужно будет убедиться, что все проверки скребков закончены, прежде чем запускать команду утилизации, если заказ имеет значение.
container.exec
синхронный или асинхронный?