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

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

问题描述

我有以下可调用函数,该函数从数据库中获取一些数据,然后使用html-pdf使用该数据创建一个PDF,将该PDF上载到存储中,最后返回存储中文件的名称. 它可以在https格式下很好地工作,但是我想将其转换为可调用的函数,由于某种原因我无法弄清楚,它因以下错误而崩溃:RangeError:超出了最大调用堆栈大小.

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(/\n/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.

您似乎在假设要返回一个嵌套了三个承诺的承诺,它将把响应发送给客户端,但这不是承诺工作的方式.您可以在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天全站免登陆