I have this code
let filter = m => m.author.id === message.author.id
const collector = message.channel.createMessageCollector({
filter,
max: 1,
time: 10000,
error: 'time'
});
let msgcontent = collector.on('collect', m => {
});
collector.on('end', collected =>{
collected.forEach((value) => {
console.log(value.content)
const msgcontent = value.content
});
});
console.log(msgcontent);
and I already get an output without even typing a message. Is the problem that I put the Collector into a constant or is it something else?
Thanks for helping!
Based on your code, and as I hinted at in my comment, it seems you are using message collectors wrong. The way your code is defining let msgcontent is not how you would go about printing the content of any message that is collected. Do something like this instead:
let filter = m => m.author.id === message.author.id
const collector = message.channel.createMessageCollector({
filter,
max: 1,
time: 10000,
error: 'time'
});
collector.on('collect', m => {
let msgcontent = m.content;
console.log("Message sent:", msgcontent);
});
collector.on('end', collected =>{
collected.forEach((value) => {
console.log("Message was collected:", value.content);
const msgcontent = value.content;
});
});
Basically, the issue was the last line in the code you provided. That line of code doesn't just wait for a message to be collected. It runs immediately after the collector and its event handlers are defined. Whatever value the collector.on() method returns by default (most likely something like EventEmitter or just undefined) was being stored in msgcontent and was being immediately printed. This misled you into believing that the collector was collecting/ending before you sent any messages.
So, I made two changes to your code:
A) I removed the let msgcontent entirely, as that is not how you get messages collected by a collector. The cause of your issue is that you were setting msgcontent equal to the output of collector.on() and immediately printing it. You confused this with actual collector output; you thought you were seeing the collected message, but you were actually just seeing an unrelated value.
B) I labeled your console.log statements. This way, you cannot get confused by which statements are being printed at which times.
Other than these issues I fixed in your code that could have potentially misled you into believing the collector was ending without collecting any messages, your collector code appears to be perfect, so I assume what I have described was the issue you were experiencing. And for future reference, please be more specific in your question; specifying what "output" you were getting would have made your question clearer.
EDIT
Based on your previous comment, it looks like you may want to use channel.awaitMessages() instead of a message collector. That will allow you to reference received messages outside the collector, assuming you use async/await. Here's an example of how you could do that, based on the code you've provided:
let filter = m => m.author.id === message.author.id
const collected = await message.channel.awaitMessages({
filter,
max: 1,
time: 10000
});
// The first and only msg in this case
const msgcontent = collected.first();
// Check if msgcontent is undefined, this means no msgs were collected
if (!msgcontent) {
// Do whatever code for when nothing is collected in time
// ...
}
Make sure to turn whatever function this code is inside into an async function, otherwise the use of await won't work. This should be much easier to implement and to read than putting all of your current code inside the collector handlers.
Related
I appreciate you looking into my problem and trying to fix it!
The problem
The problem is that whenever I try to use message.mentions inside of my code, it always returns as undefined.
What have I tried?
I have tried going into the discord.js server and asking people there to help me and they didn't help me at all.
It would be great if someone could help!
The code where the issue occurs is:
interaction.channel.awaitMessages({ messagefilter, max: 1, time: 20000, errors: ['time'] }).then(async message => {
console.log(message.mentions) // logs undefined
if (message.mentions) { // if statement fails
// this part doesn't run
} else {
// this part does run
}
}).
The TextChannel#awaitMessages() method returns a Collection of messages, not a single Message object. Simply defining it as message does not make a Collection a singular Message object, you have to get the first() property of the collection and check the mentions in that! Like so:
interaction.channel.awaitMessages({ messagefilter, max: 1, time: 20000, errors: ['time'] }).then(async message => {
console.log(message.first().mentions);
...
})
You're trying to log Collection#mentions in your console right now which won't be present.
I tried making a command in my discord.js bot, however the embed with the info gets sent BEFORE all the variables were set (this is beacuse I use Firestore, it takes a second to get the data)
The function would look something like this:
function getData(){
var level = 0
let levels = database.collection('guilds').doc(message.guild.id).collection('levels').doc(person.id)
levels.get().then((q) => {
if(q.exists){
let data = q.data()
level = data.level
return level
}
})
}
message.channel.send(getData())
// would send 0 because the function didnt get the data in time
How can I call a function and then wait for a response?
I tried messing around with async and await but I dont really know how to use them, which kept throwing errors.
I'm so confused right now. I'e spent the last hour and a bit trying to sort directories based on their creation date. Here is where I'm at:
const
fsPromises = require('fs').promises
let
arr = []
fsPromises.readdir(rootPath, 'utf8')
.then((array)=>{
array.forEach((dir)=>{
console.log(dir) // executed last for some reason
fsPromises.stat(`./Projects/${dir}`)
.then((stats)=>{
arr.push([stats.birthtimeMs, dir])
})
})
})
.then(()=>{
arr = arr.sort((a,b)=>{
Math.floor(a[0])-Math.floor(b[0])
})
})
.then(console.log(arr))
I have no idea why the final then is spitting out an unordered array.
Promises are new to me, so I'm not entirely sure if it's the promise chain that's causing the issue, but everything seems good up until the second then.
Any help would be greatly appreciated.
It is the promise chain: if the chain is broken anywhere, it won't work.
You seem to be mixing up the two arrow forms: (params) => expression and (params) => { statements }. Everything in your code is expressible with the former, so I went with that. If you convert to the statement block form, do not forget to return (which the expression form does implicitly).
Because you don't return the promises from the arrow statement block, the next promise is not stitched to the previous one, so there is no waiting going on and things execute much more synchronously than intended. Additionally, if you have multiple promises, you need to wait till they are all ready to fulfill by using Promise.all. It will create a promise that all the sub-promises are done, and return an array of results of sub-promises.
fsPromises.readdir(rootPath, 'utf8')
.then(array => Promise.all(
array.map(dir =>
fsPromises.stat(`./Projects/${dir}`)
.then(stats => [stats.birthtimeMs, dir])
)
))
.then(arr =>
arr.sort((a, b) =>
Math.floor(a[0]) - Math.floor(b[0])
)
)
.then(arr => console.log(arr))
(I did not check this, so there might be accidental dragons.)
So first readdir promises an array of files. We will map this array so that for each filename, stat makes a promise of its stats, and we chain it into a promise of tuples. So array.map now returns an array of tuple promises. Promise.all receives the array of promises, and promises an array of results of those promises when they are all done. We chain this into a promise of a sorted array, then chain it into a promise of console logging the array.
EDIT: I am not sure, but I don't think Promise.all exists in Node.js core. There is any number of promise packages for Node.js that do include Promise.all, pretty much any of them will do (e.g. Bluebird). Or, you could implement it yourself, it's not that big.
EDIT2: One could also suggest you switch to async/await if it's supported in your environment, as it makes code much more similar to what you are used to. However, some understanding of promises is still needed, as it's all promises under the hood. :P
async function statDir() {
let files = await fsPromises.readdir(rootPath, 'utf8');
let stats = [];
for (let file of files) {
let stat = await fsPromises.stat(`./Projects/${file}`);
stats.push([stat.birthtimeMs, file]);
}
stats.sort((a, b) =>
Math.floor(a[0]) - Math.floor(b[0])
);
console.log(stats);
}
statDir();
I am trying to develop an app for my fantasy baseball league to use for our draft (we some kind of quirky stuff all the major sites don't account for) - I want to pull some player data to use for the app by using MLB's API. I have been able to get the response from MLB, but can't do anything with the data after I get it back. I am trying to store the JSON into an array, and if I console.log the array as a whole, it will give me the entire chunk of data, but if I try to call the specific index value of the 1st item, it comes back as undefined.
let lastName = 'judge';
let getData = new XMLHttpRequest;
let jsonData = [];
function getPlayer () {
getData.open('GET', `http://lookup-service-
prod.mlb.com/json/named.search_player_all.bam?
sport_code='mlb'&active_sw='Y'&name_part='${lastName}%25'`, true)
getData.onload = function() {
if (this.status === 200) {
jsonData.push(JSON.parse(this.responseText));
}
}
getData.send();
console.log(jsonData);
}
When I change the above console.log to console.log(jsonData[0]) it comes back as undefined. If I go to the console and copy the property path, it displays as [""0""] - Either there has to be a better way to use the JSON data or storing it into an array is doing something abnormal that I haven't encountered before.
Thanks!
The jsonData array will be empty after calling getPlayer function because XHR loads data asynchronously.
You need to access the data in onload handler like this (also changed URL to HTTPS to avoid protocol mismatch errors in console):
let lastName = 'judge';
let getData = new XMLHttpRequest;
let jsonData = [];
function getPlayer () {
getData.open('GET', `https://lookup-service-
prod.mlb.com/json/named.search_player_all.bam?
sport_code='mlb'&active_sw='Y'&name_part='${lastName}%25'`, true)
getData.onload = function() {
if (this.status === 200) {
jsonData.push(JSON.parse(this.responseText));
// Now that we have the data...
console.log(jsonData[0]);
}
}
getData.send();
}
First answer from How to force a program to wait until an HTTP request is finished in JavaScript? question:
There is a 3rd parameter to XmlHttpRequest's open(), which aims to
indicate that you want the request to by asynchronous (and so handle
the response through an onreadystatechange handler).
So if you want it to be synchronous (i.e. wait for the answer), just
specify false for this 3rd argument.
So, you need to change last parameter in open function as below:
getData.open('GET', `http://lookup-service-
prod.mlb.com/json/named.search_player_all.bam?
sport_code='mlb'&active_sw='Y'&name_part='${lastName}%25'`, false)
But from other side, you should allow this method to act asynchronously and print response directly in onload function.
I have an anuglarJS controller that calls an API via a service to return data. Ths issue is that sometimes, the data is not being updated in a directive that uses the data that is returned.
However, digging into this resulted in observing some very strange behavior. I added several console logs to debug what was happening, and discovered that the number of items in a property on the array is changing from one console call to the next.
The controller code is as follows:
init(){
this.ftService.getSitePromise(true).then((result: ng.IHttpPromiseCallbackArg<Site>) => {
let ctrl = this;
ctrl.isLoadingItems = true;
ctrl.hideSplash = true;
ctrl.siteReady = true;
ctrl.curSite = result.data;
ctrl.curSite.Items = [];
console.log("end of header site call");
ctrl.$timeout(function () {
console.log(ctrl.curSite.Items);
console.log("start get site items first call")
ctrl.ftService.getSitePromise(false).then((result: ng.IHttpPromiseCallbackArg<Site>) => {
console.log("return first call result.data.Items: " + result.data.Items.length);
ctrl.curSite.Items = result.data.Items;
ctrl.isLoadingItems = false;
console.log("return first call ctrl.curSite.Items: " + ctrl.curSite.Items.length);
console.log(ctrl);
console.log(ctrl.curSite);
console.log(ctrl.curSite.Items);
});
}, 200);
});
}
The console from this code executing, when the data isn't being shown as expected is as follows:
Any insight as to how this is occurring, and/or how I might correct it, would be greatly appreciated.
Edit: I didn't read the comments before posting. I didn't see your problem was solved. Hopefully this may help someone else in the future??
Why/how do the elements of this array change between the console.log calls?
Objects can change in console.log calls because the deeper nested properties are accessed lazily meaning that the console will only grab them when you click the little arrow to expand the object or if they are a shallow property.
You can change this behavior by cloning the object using Object.assign though you may need to clone the object deeply (which Object.assign({}, myObj) does not.
The stackoverflow snippet console won't show the correct results. Open your chrome dev tools to see the real result.
// OPEN THE DEVELOPER TOOLS CONSOLE
let myObj = {
shallowProp: 'some value',
arr: ['initial value']
};
// notice that when this logs, it seems like the change to myObj happens before the log but it does not
console.log(
'myObj initial log',
myObj
);
// using `Object.assign` clones the object so that when expanded in the console, the value is the initial value
console.log(
'myObj initial log with Object.assign',
Object.assign({}, myObj)
);
// when the value is actually changed
myObj.arr = ['new value'];
// the final state
console.log('myObj after change', myObj);
Conclusion: try cloning your object before logging it the console.