Вы близки, но не совсем там.
Во-первых, некоторые примечания:
await
можно использовать для любого значения, но использовать его совершенно бессмысленно это на что-нибудь, что не обещание. Ваш guildMembers.map(...);
возвращает массив , а не Promise. - Смешивание
await
и .then(...)
работает, но это немного грязно. Вы уже используете await
- зачем беспокоиться о обратных вызовах? - Использование
guildMembers.map(async ...)
, как это, гарантирует, что все запросы выполняются более или менее мгновенно, и они могут завершить sh в любом порядке , Это хорошо, но это своего рода состояние гонки и приводит к более или менее случайному порядку результатов. - Это не очень хороший подход даже просто концептуально! Каждый раз, когда вам нужно выполнить l oop запросов, попробуйте найти способы сделать это только в одном запросе. SQL довольно мощный.
Причина, по которой ваш текущий код не работает, заключается в том, что ваша connection.query
функция выходит из асинхронного c потока управления . Под этим я подразумеваю, что весь смысл использования async / await и Promises заключается в основном в локальном отслеживании обратных вызовов и использовании цепочки обещаний для динамического добавления обратных вызовов. Если вы вызываете асинхронную функцию c, которая возвращает Обещание, теперь вы можете выполнять это Обещание в любом месте вашего кода и динамически присоединять к нему обработчик успеха: либо с .then()
, либо с сахаром await
.
Но функция connection.query
не возвращает Обещание, она просто заставляет вас пройти еще один голый обратный вызов - этот Обещание не отслеживается Обещанием! Обещание не имеет ссылки на этот обратный вызов, оно не может знать, когда вызывается этот обратный вызов, и, следовательно, ваш поток управления асинхронным / ожидающим завершается, и ваши обещания разрешаются задолго до выполнения запросов.
Вы можете решить эту проблему, выполнив новое обещание в функции asyn c:
async function inactiveMemberWarner() {
var msg = "```javascript\nI have sent warnings to members that have been inactive for 2 weeks.\n\n"
var inactiveMembers = '';
var count = 0;
var guildMembers = client.guilds.find(g => g.name === mainGuild).members;
const keyPromises = guildMembers.map(async (member) => {
if (isMod(member)) {
return new Promise((resolve, reject) => {
connection.query(`SELECT * from users WHERE userID='${member.id}'`, (err, data) => {
if (err) reject(err); //make errors bubble up so they can be handled
if (data[0]) {
if (!data[0].warnedForInactivity && moment().isSameOrAfter(moment(data[0].lastMSGDate).add('2', 'week'))) {
count++;
var updateWarning = {warnedForInactivity: 1}
connection.query(`UPDATE users SET ? WHERE userID='${data[0].userID}'`, updateWarning);
member.send(`**[*]** WARNING: You've been inactive on \`\`${mainGuild}\`\` for 2 weeks. Members that have been inactive for at least a month will be kicked.`);
resolve(`${count}. ${member.user.tag}\n`;);
}
} else resolve(""); //make sure to always resolve or the promise may hang
});
});
}
});
let inactiveMembersData = await Promise.all(keyPromises); // Returns an array of inactive member snippets.
inactiveMembers = inactiveMembersData.join(""); //join array of snippets into one string
}
inactiveMemberWarner();
Это будет работать, но есть намного лучший способ . SQL поддерживает оператор IN , который позволяет иметь такие условия, как WHERE userID IN (list_of_ids)
. Другими словами, вы можете сделать это в одном запросе . Вы даже можете указать больше условий, таких как warnedForInactivity = 0
и lastMSGDate BETWEEN (NOW() - INTERVAL 14 DAY) AND NOW()
. Таким образом, вы можете разгрузить все свои текущие логики обработки c на сервер SQL - то, что вы должны пытаться делать виртуально каждый раз, когда вы можете . Это также сильно упростит этот код. Я не буду go дальше, так как это выходит за рамки этого вопроса, но не стесняйтесь спрашивать другого, если вы не можете понять это.