Firebase 函数:未处理的错误 RangeError:超出最大调用堆栈大小 [英] Firebase Function: Unhandled error RangeError: Maximum call stack size exceeded

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

问题描述

我有以下可调用函数,它从我的数据库中获取一些数据,然后使用 html-pdf 使用该数据创建一个 PDF,将该 PDF 上传到存储,最后返回存储中文件的名称.它以 https 形式运行良好,但我想将其转换为可调用函数,由于某种原因我无法弄清楚,它崩溃并出现以下错误: RangeError: maximum call stack size exceeded.

I have the following callable function that gets some data from my database, then creates a PDF with that data using html-pdf, uploads that PDF to storage and finally returns the name of the file in storage. It works well in https form, but I want to convert it to a callable function, and for some reason I can't figure out, it crashes with this error: RangeError: maximum call stack size exceeded.

我怀疑这与 html-pdf 不适用于 Promise 而是使用错误/数据回调这一事实有关.但我试图将其转换为一个没有成功的承诺.

I suspect it has something to do with the fact that html-pdf doesn't work with promises and instead uses an error/data callback. But I've tried to convert it into a promise without success.

export const createPdf = functions.https.onCall((data, context) => {
    const refId = data.refId;
    const companyId = data.companyId;
    const userId = context.auth.uid;

    return admin.database().ref('/references').child(companyId).child(refId).once('value', (snapshot) => {
        const filePath = '/references/' + refId + '/pdfs/' + refId + '.pdf';
        const localeId = snapshot.child('locale').val();

        return admin.database().ref('/tags').child(localeId).once('value', (tagsSnapshot) => {
            const jsLocaleId = localeId.replace(/_/, "-");
            const projectDate = moment().locale(jsLocaleId)
                .year(snapshot.child('year').val())
                .month(snapshot.child('month').val() - 1)
                .date(15)
                .format('MMMM YYYY');

            const tags = tagsSnapshot.val();
            const projectCategories = ...
            const pictures = snapshot.child('pictures').val();

            const pdfData = {
                projectName: snapshot.child('projectName').val(),
                surface: snapshot.child('surface').val(),
                companyName: snapshot.child('companyName').val(),
                date: projectDate,
                newBuilding: snapshot.child('newBuilding').val(),
                customerName: snapshot.child('customerName').val(),
                categories: projectCategories,
                address: snapshot.child('address').val().replace(/
/g, '<br>'),
                satellite: snapshot.child('satellite').val(),
                pictures: !isNullOrUndefined(pictures) ? pictures.map((item) => {
                    return {url: item}
                }) : []
            };
            console.log("data", pdfData);
            const options = {...};

            const localTemplate = path.join(os.tmpdir(), 'share.html');
            const localPDFFile = path.join(os.tmpdir(), 'share.pdf');
            const languageCode = localeId.split("_")[0];

            return admin.storage().bucket().file('/templates/share-' + languageCode + '.html').download({destination: localTemplate}).then(() => {
                const source = fs.readFileSync(localTemplate, 'utf8');
                const html = handlebars.compile(source)(pdfData);
                pdf.create(html, options).toFile(localPDFFile, function (err, result) {
                    if (err) {
                        console.log(err);
                        throw new functions.https.HttpsError('internal', err.message);
                    }

                    return admin.storage().bucket().upload(localPDFFile, {
                        destination: filePath,
                        resumable: false,
                        metadata: {contentType: 'application/pdf'}
                    }).then((files) => {
                        console.log("files", files);
                        return files[0].getMetadata().then((metadata) => {
                            const name = metadata[0]["name"];
                            return {
                                name: name
                            };
                        });
                    }).catch(error => {
                        console.error(error);
                        throw new functions.https.HttpsError('internal', "Could not upload PDF because " + error.message);
                    });
                });
            }).catch((error) => {
                console.error("Could not download template");
                throw new functions.https.HttpsError('internal', error.message);
            });
        });
    });
});

推荐答案

可调用函数不应该只返回任何承诺.他们应该返回一个承诺,该承诺通过发送给客户端的响应来解决.你的返回一个承诺,当数据库操作完成时解决.Cloud Functions 可能正在尝试序列化 promise 中包含的值(一个 DataSnapshot 对象).这可能包含在序列化期间导致问题的循环引用.

Callable functions shouldn't return just any promise. They should return a promise that resolves with the response to send to the client. Yours is returning a promise that resolves when the database operation is complete. Cloud Functions is probably attempting to serialize the value contained in the promise (a DataSnapshot object). This might contain circular references which are causing problems during serialization.

看起来您假设返回嵌套三个 promise 的 promise 会将响应发送给客户端,但这不是 promise 的工作方式.你可以在 HTTP 函数中解决这个问题,因为你可以调用 response.send() 深层嵌套,但这在这里行不通.您将不得不取消嵌套所有承诺并连续运行它们.(你现在所做的被认为是不符合承诺的风格.)

It looks like you're assuming that returning a promise nested three promises deep is going to send the response to the client, but that's not the way promises work. You could get away with this in an HTTP function since you can call response.send() deeply nested, but that's not going to work here. You're going to have to un-nest all your promises and run them in serial. (What you're doing now is considered bad style for promises.)

这篇关于Firebase 函数:未处理的错误 RangeError:超出最大调用堆栈大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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