等待 promise 解析到与其无关的回调

Wait for promise to resolve in not related to it callback

提问人:Nitor 提问时间:7/26/2023 最后编辑:Nitor 更新时间:7/26/2023 访问量:72

问:

我正在开发一个 Steam 机器人(但问题只是关于 JS)。我有以下代码:

const steam = new SteamUser(); // from steam-user library

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function steamAddFriend(user_steam_id) {
    // 3 attempts
    for (let i = 0; i < 3; i++) {
        try {
            await steam.addFriend(user_steam_id);
            console.log('Friend request is sent');
            // everything is ok
        } catch (err) {
            if (err.eresult === SteamUser.EResult.DuplicateName) {
                if (steam.myFriends[user_steam_id] === SteamUser.EFriendRelationship.Friend) {
                    console.log('This user is already in the friends list');
                } else if (steam.myFriends[user_steam_id] === SteamUser.EFriendRelationship.RequestInitiator) {
                    console.log('The friend request has already been sent earlier');
                }
                // non-critical error, but there is no point to continue trying
            } else if ((err.eresult === SteamUser.EResult.ServiceUnavailable || err.message === 'Request timed out') && i < 2) {
                // problem with Steam servers, wait 10 seconds and try again
                await sleep(10000);
                continue;
            } else {
                // critical error. for example, user_steam_id is invalid
                // sendNotification is fetch based function to send notification to me
                await sendNotification();
            }
        }
        break; // break the loop if there is no need to continue trying
    }
    try {
        // fetch based function to send updates to backend
        await sendUpdate1(user_steam_id);
    } catch (err) {
        // handle error
    }
}

// fired when relationship with user is changed (for example, a user accepted a friend request)
steam.on('friendRelationship', async (sid, relationship) => {
    if (relationship === SteamUser.EFriendRelationship.Friend) {
        const user_steam_id = sid.getSteamID64();
        console.log('User is added to friends');
        // here I need to wait until sendUpdate1 resolves
        // sendUpdate2 is fetch based function to send updates to backend
        await sendUpdate2(user_steam_id);
    }
});

steamAddFriend('71111111111111111');

我需要回调以等待承诺解决,然后再调用。我该怎么做?请记住,机器人可以同时与多个用户一起工作。friendRelationshipsendUpdate1sendUpdate2

javascript async-await promise 回调 事件处理

评论

2赞 Krzysztof Krzeszewski 7/26/2023
这段代码完整吗?例如,您的 steamAddFriend 函数在函数定义之前甚至没有 async 关键字。这应该会导致 SyntaxError
0赞 Nitor 7/26/2023
@KrzysztofKrzeszewski对不起,代码已针对该问题进行了简化。原始代码中有一个 async 关键字。
0赞 Peter Seliger 7/26/2023
此外,(是的,这是吹毛求疵的)我会将实例与 to 一起传递,如果只是为了让观众少考虑代码。像......。steamuser_steam_idsteamAddFriendsteamAddFriend(steam, '71111111111111111')
0赞 Peter Seliger 7/26/2023
@Nitor......什么时候会被触发?'friendRelationship'
1赞 Bergi 7/26/2023
"操作顺序(首先是 sendUpdate1,然后是 sendUpdate2)被破坏。- 实际上,正确的解决方案是放弃此要求。让您的后端以任何顺序接受请求。如果出于某种原因确实存在硬性要求,请检查它并在后端(而不是在客户端代码中)强制执行它。

答:

0赞 Stichiboi 7/26/2023 #1

我不确定您为什么要在事件处理程序中等待。如果这不是等待的要求,我会这样写:friendRelationship

// fired when relationship with user is changed (for example, a user accepted a friend request)
steam.on('friendRelationship', (sid, relationship) => {
    if (relationship === SteamUser.EFriendRelationship.Friend) {
        const user_steam_id = sid.getSteamID64();
        console.log('User is added to friends');
        sendUpdate1(user_steam_id).then(() => sendUpdate2(user_steam_id));
    }
});

的处理程序立即完成,但将执行异步,一旦完成,执行 ,也是异步的。steam.onsendUpdate1sendUpdate2

评论

0赞 Nitor 7/26/2023
sendUpdate1需要在发送好友请求后立即调用(即 已解决)。 在接受好友请求时触发(只应在此之后调用)。steam.addFriendfriendRelationshipsendUpdate2
2赞 Bergi 7/26/2023 #2

听起来您想将返回的 promise 存储在按用户 ID 键控的查找映射中:steamAddFriend

const openFriendRequests = new Map();

steam.on('friendRelationship', async (sid, relationship) => {
    if (relationship === SteamUser.EFriendRelationship.Friend) {
        const userSteamId = sid.getSteamID64();
        console.log(`User is added to friends of ${userSteamId}`);
        const request = openFriendRequests.get(userSteamId);
        if (request) {
            openFriendRequests.delete(userSteamId);
            await request; // wait until sendUpdate1 resolves
            await sendUpdate2(userSteamId);
        } else {
            console.log('not a response to a friend request created by this code');
        }
    }
});

openFriendRequest.set('71111111111111111', steamAddFriend('71111111111111111'));

评论

0赞 Nitor 7/26/2023
好主意,但用户可能不接受好友请求或长时间后接受。存储 promise 可能会导致内存泄漏。
0赞 Bergi 7/26/2023
如果有很多打开的好友请求,内存可能会增长,但如果所有请求最终都被接受,内存就不会泄漏。我们在这里谈论的是多少用户?我只是隐含地假设您正在为多个不同的用户 ID 运行此代码。请求还会发生什么,当它们被拒绝时,你是否也会收到一个事件?
1赞 Bergi 7/26/2023
或者,您可以存储具有活动 update1 请求的好友请求映射,即 .然后,在处理程序中,仅当 promise 仍然存在时,您才等待它,并始终调用 .activeFriendRequests.set(id, steamAddFriend(id).finally(() => { activeFriendRequests.delete(id); }))on('friendRelationship')requestsendUpdate2
0赞 Bergi 7/26/2023
无论哪种方式,您都必须决定如何处理不是由程序操作引起的事件。你还想吗?你想忽略它们吗?您是否需要区分程序的多个单独运行(或者:如何处理服务器的重新启动)?如果这些东西很重要,你将需要某种持久性层。friendRelationshipsendUpdate2
0赞 Nitor 7/26/2023
是,当用户拒绝请求时触发。但不能保证所有请求最终都会被接受,毕竟我与真实用户打交道。我使用具有少量 RAM 的廉价 VPS,不想将其浪费在存储待处理的请求上。所有内容都存储在数据库中,事件由后端验证。您的替代建议似乎很合适。friendRelationshipfriendRelationship