Nodejs Firebase事务-超出最大调用堆栈大小 [英] Nodejs Firebase Transaction - Maximum call stack size exceeded

查看:59
本文介绍了Nodejs Firebase事务-超出最大调用堆栈大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我具有云功能,该功能使用事务更新游戏中的玩家.当/players为null时,我试图返回一个Map,但出现超出最大调用堆栈大小"的信息.

I have a cloud function that uses a transaction to updates the players in a game. When the /players is null, i am trying to return a Map, but i get "Maximum call stack size exceeded".

这是我的云功能:

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

如何将地图设置为/players节点?

How can i set a map to /players node?

代码有多个问题,正如@Renaud指出的那样,我已将'once'回调更改为使用promises版本. 我在事务中发送回数据时也遇到了问题.我发送的数据使用的是复杂的JS对象(例如Map()),但经过一番挣扎(在语法上),我将其更改为普通的JS对象(类似于json的结构).请在下面查看我的更改:

There were more than one issues with the code and as @Renaud pointed out, i have changed the 'once' callback to use promises version. Also i had issues sending back data in the transaction. The data that i sent was using complex JS objects like Map(), but after some struggle (with the syntax) i changed it to a normal JS object (json like structure). Please see my changes below:

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对象,请参阅https://stackoverflow.com/a/52569728/337186​​2 .

Your problem most probably comes from the fact you are returning a complex JavaScript object, see https://stackoverflow.com/a/52569728/3371862.

此外,请注意,您应该使用

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 => {...});

通过此操作,您将能够正确构建承诺链将被退回.同样,当处理players值周围的不同情况时,不是返回将在.then((result) => {...})块中处理的JavaScript对象(这不是必需的,也不是真正合乎逻辑的),而是抛出将在.then((result) => {...})块中处理的错误. c4>阻止.

With this you will be able to correctly build the promise chain to be returned. Also, when dealing with the different cases around the players value, instead of returning JavaScript objects that will be handle in the .then((result) => {...}) block (which is not necessary and not really logical), throw errors that will be handled in the catch() block.

以下内容:

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);
      }
    });
});

请参阅此处,以获取有关如何处理可调用对象中的错误的更多详细信息.云功能.

See here for more details on how to handle errors in a Callable Cloud Function.

这篇关于Nodejs Firebase事务-超出最大调用堆栈大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆