Вы должны отцепить Promise
с, которые вы получаете.Client#fetchUser()
возвращает Promise
, который вы ожидаете , но недостаточно.Вы должны размножаться до Promise
с.Если что-то в вашей цепочке вызовов функций асинхронно, вы должны рассматривать всю цепочку как асинхронную.
Вы заполняете this.list
из fetchUser(...).then(...)
, что не обязательно плохо, если вы этого не делаетепытайтесь использовать list
до тех пор, пока не будет завершена цепочка разрешений fetchUser
.Вы этого не делаете;ты сразу resolve(this.list)
.
Рассмотрите эту сокращенную форму вашей исходной функции:
return new Promise((resolve, reject) => {
this.list = [];
for (let i in users) {
// A promise is created right here
client.fetchUser(i).then((user) => {
// This will populate list AFTER the then callback
this.list.push([user.username.substring(0, 13), score])
});
}
// You aren't waiting until the promise created by fetchUser completes
resolve(this.list);
})
this.list
нельзя считать завершенной, пока все участвующие пользователи не загрузят свои профили и не получат свои оценки.Учитывая это, мы можем использовать Promise.all()
, который принимает массив Promise
s, а затем разрешает, как только все предоставленные обещания разрешены.Поэтому, чтобы подождать таким образом, мы бы сделали что-то подобное, что все еще не идеально, но ждет правильно:
return new Promise((resolve, reject) => {
this.list = [];
// This is an array of Promises
const discordUsersPromise = users.map(user => client.fetchUser(user));
// Wait till all the fetchUser calls are done
const listIsPopulatedPromise = Promise.all(discordUsersPromise).then(dUsers => {
// This replaces your for (let i in users) {}
Object.entries(users).forEach((user, idx) => {
const score = this.getScore(user);
const discordUser = dUsers[idx];
this.list.push([discordUser.username.substring(0, 13), score])
});
});
// We still have to wait for the list to be completely populated
return listIsPopulatedPromise.then(() => this.list);
})
Рассмотрим эту реализацию.Я сделал некоторые предположения о вашем коде, так как вы используете this.list
, но не включаете то, к чему относится this
, но большая часть должна быть такой же:
/**
* Object to composite certain user properties
* @typedef {RealUser}
* @property {String} user The local string for the user
* @property {User} realUser The user that Discord gives us
* @property {Number} score The score this user has
*/
/**
* Class to encapsulate user and score and data
*/
class Game {
/**
* Constructs a game
*/
constructor() {
/**
* The users we are keeping score of
* @type {Object}
*/
this.users = {};
}
/**
* Get the score of a particular user
* @param {String} user User to get score of
* @returns {Number} User's score
*/
getScore(user) {
return this.users[user] || 0;
}
/**
* Get a composite of users and their status
* @param {String[]} users The users to put on our leaderboard
* @returns {Promise<RealUser[]>} Sorted list of users that we included in our leaderboard
*/
getLeaderBoard(users) {
// Map all the users that we are given to Promises returned bye fetchUser()
const allRealUsersPromise = Promise.all(users.map(user => client.fetchUser(user)
/*
* Create an object that will composite the string that we use
* to note the user locally, the Discord User Object, and the
* current score of the user that we are tracking locally
*/
.then(realUser => ({
user,
realUser,
score: this.getScore(user)
}))));
/*
* Once we have all the data we need to construct a leaderboard,
* we should sort the users by score, and hand back an array
* of RealUsers which should contain all the data we want to
* print a leaderboard
*/
return allRealUsersPromise
.then(scoredUsers => scoredUsers.sort((a, b) => a.score - b.score));
}
/**
* Prints out a leaderboard
* @param {String[]} users The users to include on our leaderboard
*/
printLeaderBoard(users) {
// Go get a leaderboard to print
this.getLeaderBoard(users).then(sortedScoredUsers => {
// Iterate our RealUsers
sortedScoredUsers.forEach((sortedScoredUser, idx) => {
const username = sortedScoredUser.realUser.username;
const score = sortedScoredUser.score;
// Print out their status
console.log(`${username.substring(0, 13)} is in position ${idx + 1} with ${score} points`);
});
});
}
}
const game = new Game();
game.users["bob"] = 5;
game.users["sue"] = 7;
game.users["tim"] = 3;
game.printLeaderBoard(Object.keys(game.users));