首页 > 解决方案 > Firebase 云函数:“未处理的错误 RangeError:超出最大调用堆栈大小”

问题描述

我有一个使用 firebase 的云功能,从我的 Angular 应用程序调用它后,我收到上述错误:

Unhandled error RangeError: Maximum call stack size exceeded
    at baseKeys (/workspace/node_modules/lodash/lodash.js:3483:12)
    at keys (/workspace/node_modules/lodash/lodash.js:13333:60)
    at /workspace/node_modules/lodash/lodash.js:4920:21
    at baseForOwn (/workspace/node_modules/lodash/lodash.js:2990:24)
    at Function.mapValues (/workspace/node_modules/lodash/lodash.js:13426:7)
    at encode (/workspace/node_modules/firebase-functions/lib/providers/https.js:184:18)
    at /workspace/node_modules/lodash/lodash.js:13427:38
    at /workspace/node_modules/lodash/lodash.js:4925:15
    at baseForOwn (/workspace/node_modules/lodash/lodash.js:2990:24)
    at Function.mapValues (/workspace/node_modules/lodash/lodash.js:13426:7

我已经搜索堆栈以找到解决方案 - 但在大多数情况下存在序列化问题,我相信这里不会发生。

这是我的功能:

    exports.createCase = functions.region('europe-west2').https.onCall((data, context) => {
    console.log("creating new case");

    if (!context.auth) {
        throw new functions.https.HttpsError('failed-precondition', 'This function must be called ' +
            'while authenticated.');
    }

    const caseName = data.caseName;
    // Authentication / user information is automatically added to the request.
    const uid = context.auth.uid;
    const name = context.auth.token.name || null;
    const picture = context.auth.token.picture || null;
    const email = context.auth.token.email || null;

    console.log("caseName=" + caseName + " uid=" + uid + " name=" + name + " picture=" + 
      picture + " email=" + email);

    var operationResult = new Promise ((resolve, reject) => {
      var accessData : any = {};      
      var accessId = admin.database().ref('/access/').push();
      var operationId = admin.database().ref('/operationslog/' + accessId.key + '/').push();
      console.log("accessId created=" + accessId + ' ||  ' + accessId.key + ' operations id=' +
       operationId + ' || ' + operationId.key);
            
      let now: number = Date.now();
      accessData[`/access/` + accessId.key] = new Access(caseName, uid, email);
      accessData[`/operationslog/` + accessId.key + `/` + operationId.key] = {
        date: now,
        performedByUser: uid,
        performedByMail: email,
        performedByImg: picture,
        performedBySystem: false,
        operationType: 'CREATE',
        order: (REVERSE_ORDER_MAX - now),
        details: {creator: uid, name: caseName}
      };
      console.log('commiting data');
      admin.database().ref().update(accessData).then( (value: void) => {
          console.log("returning ok result");
          resolve({
            status: "Ok",
            accessId: accessId,
            description: 'Case created'
          });
        }, err => {
          console.log("Error while trying to create case: " + err);
          reject("CASE NOT CREATED");
        }
      ).catch(exception => {
          console.log("Error while trying to create case: " + exception);
          reject("CASE NOT CREATED");
        }
      );
    }
    );

    return operationResult;
  });

以及来自 Angular 应用程序的调用:

let createCaseCall = functions.httpsCallable('createCase');
createCaseCall({caseName: value.caseName}).then(result => {
      // Read result of the Cloud Function.
      console.log("got result: " + result);
      if (result.data.status == 'Ok') {
        this.showSuccessMessage('Case created.');
      }
    }).catch(err => {
      console.log("Error while calling cloud functions: " + err);
      this.showErrorMessage('Error while creating the case.');
    });

现在,重要的信息是,调用此函数时会创建 firebase 实时数据库中的数据,并且控制台日志确实包含“returning ok result”行...

标签: javascriptfirebasefirebase-realtime-databasegoogle-cloud-functions

解决方案


它仍然是一个序列化问题。

这是您要发送回客户端的内容:

          resolve({
            status: "Ok",
            accessId: accessId,
            description: 'Case created'
          });

accessId是推送操作的结果:

  var accessId = admin.database().ref('/access/').push();

这意味着它是一个 DatabaseReference 对象,其中包含无法序列化的循环引用。它不是一个简单的数据类型,如字符串。

您需要更仔细地考虑您想要发送回客户端应用程序的确切内容。也许您想发回由 ? 创建的子密钥的名称或路径push()

此外,您可能希望删除整个new Promise()内容,因为这是一种反模式。当您拥有来自所有其他可用数据库操作的承诺时,无需创建新承诺。


推荐阅读