繁体   English   中英

Nodejs Firebase 事务 - 超出最大调用堆栈大小

[英]Nodejs Firebase Transaction - Maximum call stack size exceeded

我有一个云 function,它使用事务来更新游戏中的玩家。 当 /players 为 null 时,我试图返回 Map,但我得到“超出最大调用堆栈大小”。

这是我的云 function:

export const addUserToGame = functions.https.onCall((data, context) => {

    // Expected inputs - game_id(from data) and UID(from context)

    if (context.auth == null) {
        return {
            "status": 403,
            "message": "You are not authorized to access this feature"
        };
    }

    const uid = context.auth.uid;
    const game_id = data.game_id;

    let gameIDRef = gamesRef.child(game_id);
    return gameIDRef.once("value", function (snapshot) {

        let players: Map<String, Number> = snapshot.child("players").val();
        let max_players: Number = snapshot.child("max_players").val();
        if (players != null && players.has(uid)) {
            return {
                "status": 403,
                "message": "Player already in the game"
            }
        } else if (players != null && players.size >= max_players) {
            return {
                "status": 403,
                "message": "Game is already full"
            }
        } else {
            let playersNodeRef = gamesRef.child(game_id).child("players");
            return playersNodeRef.transaction(t => {

                if (t === null) {
                    return new Map<String, Number>().set(uid, 1);//trying to set a map with the player data, when the /players is null
                } else {
                    let playersData: Map<String, Number> = t;
                    if (playersData.size >= max_players) { // rechecking
                        return;
                    } else {
                        playersData.set(uid, 1);
                        return playersData;
                    }
                }

            }).then(result => {
                if (result.committed) { // if true there is a commit and the transaction went through
                    return {
                        "status": 200,
                        "message": "User added to game successfully"
                    }
                } else {
                    return {
                        "status": 403,
                        "message": "Unable to add user at this time. Please try again"
                    }
                }
            }).catch(error => {
                return {
                    "status": 403,
                    "message": error
                }
            });
        }
    });
});

这是堆栈跟踪:

addUserToGame
Function execution took 1423 ms, finished with status code: 500
at /workspace/node_modules/lodash/lodash.js:13401:38
at encode (/workspace/node_modules/firebase-functions/lib/providers/https.js:179:18)
at Function.mapValues (/workspace/node_modules/lodash/lodash.js:13400:7)
at baseForOwn (/workspace/node_modules/lodash/lodash.js:2990:24)
at /workspace/node_modules/lodash/lodash.js:4900:21
at keys (/workspace/node_modules/lodash/lodash.js:13307:14)
at isArrayLike (/workspace/node_modules/lodash/lodash.js:11333:58)
at isFunction (/workspace/node_modules/lodash/lodash.js:11653:17)
at baseGetTag (/workspace/node_modules/lodash/lodash.js:3067:51) 
at Object (<anonymous>)
Unhandled error RangeError: Maximum call stack size exceeded

如何将 map 设置为/players节点?


代码存在不止一个问题,正如@Renaud 指出的那样,我已将“一次”回调更改为使用 Promise 版本。 我在交易中发回数据时也遇到了问题。 我发送的数据使用的是复杂的 JS 对象,例如 Map(),但经过一番努力(使用语法),我将其更改为普通的 JS object(类似 json 的结构)。 请在下面查看我的更改:

if (t === null) {
                    return [{ [uid]: { "status": 1 } }]; // if null, create an array and add an object to it
                } else {
                    let playersData = t;
                    if (playersData.size >= max_players) { // rechecking
                        return;
                    } else { // if not null create an object and add to the existing array
                        playersData.push({ 
                            [uid]: {
                                "status": 1
                            }
                        });
                        return playersData;
                    }
                }

您的问题很可能来自您返回复杂 JavaScript object 的事实,请参阅https://stackoverflow.com/a/525697283/

In addition , note that you should use the promise version of the once() method, since, in a Callable Cloud Function you must return a promise that resolves with the data object to send back to the client.

而不是做

return gameIDRef.once("value", function (snapshot) {...});

return gameIDRef.once("value").then(snapshot => {...});

有了这个,您将能够正确构建要返回的promise 链 此外,在处理围绕players价值的不同情况时,而不是返回将在 .then .then((result) => {...})块中处理的 JavaScript 对象(这不是必需的,也不是真正合乎逻辑的),抛出将在catch()块中处理的错误。

大致如下:

export const addUserToGame = functions.https.onCall((data, context) => {
  // Expected inputs - game_id(from data) and UID(from context)

  if (context.auth == null) {
    return {
      status: 403,
      message: 'You are not authorized to access this feature',
    };
    // IMHO better to do  throw new functions.https.HttpsError('...', ...);
  }

  const uid = context.auth.uid;
  const game_id = data.game_id;

  let gameIDRef = gamesRef.child(game_id);
  return gameIDRef
    .once('value')
    .then((snapshot) => {
      let players: Map<String, Number> = snapshot.child('players').val();
      let max_players: Number = snapshot.child('max_players').val();

      if (players != null && players.has(uid)) {
        throw new Error('Player already in the game');
      } else if (players != null && players.size >= max_players) {
        throw new Error('Game is already full');
      } else {
        let playersNodeRef = gamesRef.child(game_id).child('players');
        return playersNodeRef.transaction((t) => {
          if (t === null) {
            return new Map<String, Number>().set(uid, 1); //trying to set a map with the player data, when the /players is null
          } else {
            let playersData: Map<String, Number> = t;
            if (playersData.size >= max_players) {
              // rechecking
              return;
            } else {
              playersData.set(uid, 1);
              return playersData;
            }
          }
        });
      }
    })
    .then((result) => {
      if (result.committed) {
        // if true there is a commit and the transaction went through
        return {
          status: 200,
          message: 'User added to game successfully',
        };
      } else {
        // probably throw an error here
        return {
          status: 403,
          message: 'Unable to add user at this time. Please try again',
        };
      }
    })
    .catch((error) => {
      if (error.message === 'Player already in the game') {
        throw new functions.https.HttpsError('...', error.message);
      } else if (error.message === 'Game is already full') {
        throw new functions.https.HttpsError('...', error.message);
      } else {
        throw new functions.https.HttpsError('internal', error.message);
      }
    });
});

有关如何处理 Callable Cloud Function 中的错误的更多详细信息,请参阅此处

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM