typescript - Fungsi Firebase: Kesalahan tidak tertangani RangeError: Ukuran tumpukan panggilan maksimum terlampaui
Saya memiliki fungsi callable berikut yang mengambil beberapa data dari database saya, kemudian membuat PDF dengan data tersebut menggunakan html-pdf, mengunggah PDF itu ke penyimpanan, dan akhirnya mengembalikan nama file di penyimpanan. Fungsi ini bekerja dengan baik dalam bentuk https, tetapi saya ingin mengubahnya menjadi fungsi callable, dan untuk beberapa alasan yang tidak bisa saya mengerti, fungsi ini mengalami crash dengan error ini: RangeError: maximum call stack size exceeded.
Saya curiga ini ada hubungannya dengan fakta bahwa html-pdf tidak bekerja dengan promise dan malah menggunakan callback error/data. Tapi saya sudah mencoba mengubahnya menjadi promise tanpa berhasil.
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);
});
});
});
});
Jawaban #1
Fungsi yang dapat dipanggil seharusnya tidak mengembalikan sembarang promise. Mereka seharusnya mengembalikan promise yang menyelesaikan dengan respons yang akan dikirim ke klien. Promise Anda mengembalikan promise yang selesai ketika operasi basis data selesai. Cloud Functions kemungkinan sedang mencoba untuk men-serialize nilai yang terkandung dalam promise (objek DataSnapshot). Ini mungkin mengandung referensi melingkar yang menyebabkan masalah selama proses serialisasi.
Sepertinya Anda mengasumsikan bahwa mengembalikan sebuah promise yang bersarang tiga tingkat promise akan mengirimkan respons ke klien, tetapi itu bukan cara kerja promise. Anda mungkin bisa melakukannya dalam fungsi HTTP karena Anda bisa memanggil response.send() walaupun bersarang dalam tingkat yang dalam, tapi itu tidak akan berhasil di sini. Anda harus melepaskan semua promise Anda dari nesting dan menjalankannya secara serial. (Apa yang Anda lakukan sekarang dianggap sebagai gaya buruk untuk promise.)