代码之家  ›  专栏  ›  技术社区  ›  Frustrated programmer

如何创建异步代码?

  •  -1
  • Frustrated programmer  · 技术社区  · 8 年前

    嘿,所以我用 discord.js 我想显示用户的名字而不是他们的ID 不一致的 我用这个函数 .fetchUser(ID)

    .fetchuser(id) 是一个承诺,可能需要一些时间,取决于带宽。

    所以因为 不一致的 使用一个承诺我的代码不再是异步的,我 思想 通过将代码放入一个承诺中,它将运行异步。

    我错了。

    我的代码:

    //This is ran inside a .prototype function so (this) is defined
    return new Promise((resolve, reject) => {
        this.list = [];
        //users is an object with user's IDs as the key
        //Currently it only has one key in it (mine)
        for (let i in users) {
                let pos = 0;
                let score = this.getScore(users[i]);
                if (score === 0) {
                    client.fetchUser(i).then((user)=> {
                        console.log(`pushed`);//logs way after the "finish" is logged 
                        this.list.push([user.username.substring(0,13), score])
                    });
                    continue;
                }
                for (let h = 0; h < this.list.length; h++) {
                    if (score >= this.list[h][1]) {
                        pos = h;
                        break;
                    }
                }
                client.fetchUser(users[i].id).then((user) => {
                    this.list.splice(pos, 0, [user.username.substring(0,13), score])
                })
            }
            console.log(`Finished: `+this.list.length);
            resolve(this.list);
    })
    
    1 回复  |  直到 8 年前
        1
  •  0
  •   zero298    8 年前

    你必须把 Promise 你收到了。 Client#fetchUser() 返回A 许诺 哪一个你 等待,但还不够。你必须宣传 许诺 如果你的函数调用链中的某些东西是异步的,你应该考虑整个链是异步的。

    你填满 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);
    })
    

    这个列表 在所有相关用户都加载了他们的个人资料并检索了他们的分数之前,不能认为是“完整的”。考虑到这一点,我们可以利用 Promise.all() 它需要一系列 许诺 然后在所有提供的承诺都解决后再解决。所以要这样等待,我们会做这样的事情,这仍然不是理想的,但是等待是正确的:

    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 是的一个实例,但大多数应该是相同的:

    /**
     * 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));