Loop not pushing game data to Array before console dump? - javascript

I have been looking over this code and redoing it so often trying to get this loop to work. I have also come to realise i am getting 429 error from s3 which i have to look into to. in the matter at hand, i am attempting to save the data to my gameData Array and then output via console after the promise has fulfilled so ultimately the promise variable can return the array. But i am not able to get all the data to store in the array. Something todo with the loop i guess but i cant pin point it. Any ideas?
var getGameData = function(matchIdArray) {
var promise = new Promise(function(resolve,reject) {
s3.headObject({Bucket: 'lolsearchgames', Key: '347341'}, function(err, result) {
if (err && err.code === 'NotFound') {
var gameData = new Array();
for (var i = 0; i < matchIdArray.length; i++) {
gameData.push(new Promise(function(res, rej) { lolapi.Match.get(matchIdArray[i], function (error, gamedata) {
if (error) {
rej(error);
}
if (gamedata) {
res(gamedata);
}
})})
.then(function() {
if (i === 9) {
console.log(gameData);
resolve(gameData);
}
}));
}
} else {
// Retrieve from s3 Bucket
}
})
});
return promise;
};

Here's a snippet of your code that needs to be rewritten
if (err && err.code === 'NotFound') {
var gameData = new Array();
for (var i = 0; i < matchIdArray.length; i++) {
gameData.push(new Promise(function(res, rej) {
lolapi.Match.get(matchIdArray[i], function(error, gamedata) {
if (error) {
rej(error);
}
if (gamedata) {
res(gamedata);
}
})
})
);
}
Promise.all(gamedata).then(function() {
console.log(gameData);
resolve(gameData);
});
}
This uses Promise.all on the array of gamedata promises to wait for them to all resolve before resolving the "main" promise
Note: this is a quick and dirty fix, the code may be able to be rewritten to remove the need for the outer new Promise - however, without seeing the code foe the else that's only a speculation on my part
Still "quick and dirty", but a better rewrite
if (err && err.code === 'NotFound') {
Promise.all(matchIdArray.map(function(matchId) {
return new Promise(function(res, rej) {
lolapi.Match.get(matchId, function(error, gamedata) {
if (error) {
rej(error);
}
if (gamedata) {
res(gamedata);
}
})
});
})).then(function() {
console.log(gameData);
resolve(gameData);
});
}

Related

how to get return value of promise

Here is a function to find mx records of a service and i need to save the one value(with the lowest priority) to make a request to it. How can I save and return this value?
const dns = require('dns');
const email = '...#gmail.com'
let res = email.split('#').pop();
function getMxRecords(domain) {
return new Promise(function(resolve, reject) {
dns.resolveMx(domain, function(err, addresses) {
if (err) {
//console.log(err, err.stack)
resolve(null);
} else {
//console.log(addresses);
let copy = [...addresses];
//console.log(copy);
let theone = copy.reduce((previous, current) => {
if (previous.priority < current.priority) {
return current;
}
return previous;
});
resolve(theone);
}
});
});
}
let a = getMxRecords(res);
console.log(a);
Yeah, so i need to export this module to make a request to it like below;
let socket = net.createConnection(25, request(email), () => {})
so for this my function should request me or array or object with only one value, when i'm trying it doesn't work, i always get this:
Promise { } //HERE IS RETURN FROM MY FUNCTION (WITH .THEN)
Error in socket connect ECONNREFUSED 127.0.0.1:25
A Promise is mostly an asynchronous call. It returns an Promise-Object that will resolve or reject the Promise. To access the result, you will call some functions:
function getMxRecords(domain) {
return new Promise(function(resolve, reject) {
dns.resolveMx(domain, function(err, addresses) {
if (err) {
//console.log(err, err.stack)
resolve(null);
} else {
//console.log(addresses);
let copy = [...addresses];
//console.log(copy);
let theone = copy.reduce((previous, current) => {
if (previous.priority < current.priority) {
return current;
}
return previous;
});
resolve(theone);
}
});
});
}
getMxRecords(res)
.then(yourResolveValueProvided => {
// Your code if the promise succeeded
})
.catch(error => {
// Your code if the promises reject() were called. error would be the provided parameter.
})

Calling a promise function recursively

I'm trying to call a promise function recursively.
The following call service.getSentenceFragment() returns upto 5 letters from a sentence i.e. 'hello' from 'helloworld. Providing a nextToken value as a parameter to the call returns the next 5 letters in the sequence. i.e. 'world'. The following code returns 'hellohelloworldworld' and does not log to the console.
var sentence = '';
getSentence().then(function (data)) {
console.log(sentence);
});
function getSentence(nextToken) {
return new Promise((resolve, reject) => {
getSentenceFragment(nextToken).then(function(data) {
sentence += data.fragment;
if (data.nextToken != null && data.nextToken != 'undefined') {
getSentence(data.NextToken);
} else {
resolve();
}
}).catch(function (reason) {
reject(reason);
});
});
}
function getSentenceFragment(nextToken) {
return new Promise((resolve, reject) => {
service.getSentenceFragment({ NextToken: nextToken }, function (error, data) {
if (data) {
if (data.length !== 0) {
resolve(data);
}
} else {
reject(error);
}
});
});
}
Cause when you do this:
getSentence(data.NextToken);
A new Promise chain is started, and thecurrent chain stays pending forever. So may do:
getSentence(data.NextToken).then(resolve, reject)
... but actually you could beautify the whole thing to:
async function getSentence(){
let sentence = "", token;
do {
const partial = await getSentenceFragment(token);
sentence += partial.fragment;
token = partial.NextToken;
} while(token)
return sentence;
}
And watch out for this trap in getSentenceFragment - if data is truthy but data.length is 0, your code reaches a dead end and the Promise will timeout
// from your original getSentenceFragment...
if (data) {
if (data.length !== 0) {
resolve(data);
}
/* implicit else: dead end */
// else { return undefined }
} else {
reject(error);
}
Instead, combine the two if statements using &&, now our Promise will always resolve or reject
// all fixed!
if (data && data.length > 0)
resolve(data);
else
reject(error);
You could recursively call a promise like so:
getSentence("what is your first token?")
.then(function (data) {
console.log(data);
});
function getSentence(nextToken) {
const recur = (nextToken,total) => //no return because there is no {} block so auto returns
getSentenceFragment(nextToken)
.then(
data => {
if (data.nextToken != null && data.nextToken != 'undefined') {
return recur(data.NextToken,total + data.fragment);
} else {
return total + data.fragment;
}
});//no catch, just let it go to the caller
return recur(nextToken,"");
}
function getSentenceFragment(nextToken) {
return new Promise((resolve, reject) => {
service.getSentenceFragment({ NextToken: nextToken }, function (error, data) {
if (data) {
if (data.length !== 0) {
resolve(data);
}
} else {
reject(error);
}
});
});
}

Mongoose fetching data, setting in a list (undefined)

I am trying to fetch all messages of two users from MongoDB using a custom function. I am not able to set list. I have no idea how to use callback here.
fetchall = function(chatuser, me) {
var allmessage = [] ;
var promise = this.model.find({},function(err, docs) {
if (err) { return console.error(err);
}
console.log(docs); //working
// doSomethingElse(docs);
allmessage = JSON.stringify(docs);
return allmessage;
}).exec();
// promise.then(doSomethingElse(cc));
console.log('all',allmessage); // undefined
return allmessage;
};
here is Example
fetchall = function(chatuser, me, cb) {
var allmessage = [];
var promise = this.model.find({}, function(err, docs) {
if (err) {
// here cb is callback
return cb(err)
} else {
console.log(docs); //working
if (Array.isArray(docs) && docs.length > 0) {
// do it here
cb(null, docs)
} else {
// throw error here
// no result found like
cb()
}
}).exec();
};

Why line 27 is executed before line 24?

I m beginner with javascript and try to build a clean object to use the Dockerode library in my use Case. I have an async problem here my line 27 executes before 24 and I don't understand why and how to fix it!
Also if it's easier for you, please visit this public gist: 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;
If your chaining promises, don't forget to return your next promise..
eg..
.then(()=>{
this.checkReadyScraper();
}
If checkReadyScraper() is a promise, then you will want to return it.
eg.
.then(()=>{
return this.checkReadyScraper();
}
Otherwise all your doing is running checkReadyScraper() and totally ignoring the returned Promise.
Here is how I think your runExec should look. I'm assuming the exec.inspect is what you want to resolve on.
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
});
};
Executing a task (with container.exec, on line 81 in your code), runs asynchronious from the rest of your steps, having a callback when they are done.
You would have to make sure all the checks for scrapers are finished before running the scrap command if order matters.
First - there is no need to handle errors in every then() call.
You can implement a single error catch, which will catch error in any then() item in a sequence:
.then(()=> {
this.checkReadyScraper();
})
.then(() => {
this.scrap();
})
.catch(e => console.log(e))
Also note, that arrow function like catch(e => console.log(e)) doesn't require {} and ;
Your problem is that your task is Async. If you want to chain tasks - you should make a task to return a Promise
This is a rough exaple what you should refactor:
//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)
});
}
After this, you will be able to chain Promises (which is quite awesome actually and helps to solve callback hell):
.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))
Also, to make this looks cleaner I even recommend to do some small refactoring, which will finally give you oneliner:
.then(this.checkReadyScraper).then(this.scrap).catch(console.log)

Bluebird.js Promise not chaining .then()s

socket.on('new video', function(data) {
console.log("New video")
var addToCueP = Promise.promisify(addToCue)
var getCueFromDbP = Promise.promisify(getCueFromDb)
addToCueP(data.id, socket.nickname)
.then(function() {
return getCueFromDbP();
})
.then(function() {
console.log("Emit change video")
io.sockets.emit('change video', {id: data.id, title: data.title, nick: socket.nickname});
})
});
I am using Bluebird for promises but I'm having issues, the addToCue function gets called but the other functions after for example the getCuefromDb and the Socket emitter isnt getting triggered, does anyone have any idea where I'm going wrong?
addToCue()
var addToCue = function(id, user) {
console.log("Add to cue")
var video = new Video();
video.id = id;
video.user = user;
video.save(function(err, data) {
if (err) {
console.log(err)
} else {
return true
}
});
}
getCueFromDb()
var getCueFromDb = function(callback) {
Video.find({}).exec(function(err, videos) {
if (err) {
return err;
}
if (videos.length) {
cue = []; // empty array
videos.forEach(function(video) {
cue.push(video.id) // push all the videos from db into cue array
});
io.sockets.emit('send cue', {cue: cue});
console.log("getCueFromDb", cue)
if (callback) {
callback();
return
}
else {
return
}
}
else {
cue = [];
io.sockets.emit('send cue', {cue: cue});
console.log("getCueFromDb (no videos)", cue)
if (callback)
callback()
else
return
}
})
}
addToCue() does not accept the right types of arguments in order to call .promisify() on it- therefore promisify does not work properly with it.
The last argument to addToCue() MUST be a callback that is called when the async operation is done and the first argument to that callback must be an error value and the second argument can be a result.
Now, since you control the code to addToCue(), you should just change its code to return a promise that is resolved/rejected based on the underlying async operation and not use .promisify() at all.
var addToCue = function(id, user) {
return new Promise(function(resolve, reject) {
console.log("Add to cue")
var video = new Video();
video.id = id;
video.user = user;
video.save(function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
Alternatively, you could use .promisify() on video.save() or .promisifyAll() on the Video object.
Or, if your Video interface already supports promises, you can just use it's native support and return that promise.

Categories

Resources