打印收据的函数如下:
const generateOnePDF = async (ticket, orderData) =>
new Promise(async (resolve, reject) => {
let finalString = '';
if (!ticket) {
const err = new Error('missing ticket data');
reject(err);
}
if (!orderData) {
const err = new Error('missing order data');
reject(err);
}
const { payload, sequence, title } = ticket;
if (!payload) {
const err = new Error('missing payload data');
reject(err);
}
if (!sequence) {
const err = new Error('missing ticket sequence');
reject(err);
}
if (!title) {
const err = new Error('missing ticket book title');
reject(err);
}
const doc = new PDFDocument();
PDFDocument.prototype.addSVG = function (svg, x, y, options) {
return SVGtoPDF(this, svg, x, y, options);
};
const stream = doc.pipe(new Base64Encode());
// logo
const logo = await fetchImage(
`${url}/static/media/logo_157.c15ac239.svg`
);
doc.addSVG(logo.toString(), 32, 40, {});
doc.moveUp();
doc.moveUp();
doc.text(`Order: O${orderData.orderId}`, { align: 'right' });
const rectXOffset = 25;
const rectPosition = 32;
doc
.rect(rectXOffset, rectPosition, doc.page.width - rectXOffset * 2, 32)
.stroke();
doc.moveDown();
doc.moveDown();
doc.moveDown();
doc.rect(rectXOffset, 80, doc.page.width - rectXOffset * 2, 680).stroke();
doc.text(orderData.title, { align: 'left' });
doc.moveDown();
doc.text(orderData.venue, { align: 'left' });
doc.text(orderData.address, { align: 'left' });
doc.text(`${orderData.city}, ${orderData.province} ${orderData.zip}`, {
align: 'left'
});
if (orderData.custom) {
doc.moveDown();
doc.text(orderData.customCopy, { align: 'left' });
}
doc.moveDown();
doc.text(`${orderData.date} at ${orderData.show}`, { align: 'left' });
doc.moveDown();
const image = await fetchImage(orderData['image']);
doc.image(image, {
fit: [100, 100],
align: 'right',
valign: 'top'
});
doc.moveDown();
doc.text(
`Order: O${orderData.orderId}. Placed by ${orderData.firstName} ${orderData.lastName} on ${orderData.created}`
);
// right column
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.moveUp();
doc.text(`Ticket: ${sequence} ${title}`, { align: 'right' });
const qrcode = new QRCode({
content: payload,
width: 200,
height: 200,
xmlDeclaration: false,
join: true
}).svg();
const options = { align: 'right' };
doc.addSVG(qrcode, 400, 150, options);
// finalize/close the PDF file
doc.end();
stream.on('data', (chunk) => {
finalString += chunk;
});
stream.on('end', () => {
// the stream is at its end, so push the resulting base64 string to the response
resolve(finalString);
});
stream.on('error', (err) => {
reject(err);
});
});
这不是世界上最干净的一段代码,但它同时为我工作。
我在下面为这段代码添加了一个简单的单元测试:it('should throw an error if the ticket or order data are invalid', async () => {
await expect(generateOnePDF(null, {})).rejects.toThrowError();
await expect(generateOnePDF({}, null)).rejects.toThrowError();
});
测试通过了,但是它写了"垃圾"。到控制台。代码中有未处理的拒绝
(node:53703) UnhandledPromiseRejectionWarning: TypeError: Cannot destructure property 'payload' of '((cov_2gbfm4phuo(...).s[25]++) , ticket)' as it is null.
(Use `node --trace-warnings ...` to show where the warning was created)
是其中一个错误,另一个是:
(node:53703) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'orderId' of null
我不明白为什么if (!ticket) throw()
和if (!orderData) throw
不能阻止错误的发生。"破碎的承诺"在哪里?
我在承诺处理程序函数内做reject
,所以我不在哪里做?
问题是您的函数在reject
被调用后仍然继续执行,因此发生错误。
此外,您不应该使用async
回调创建new Promise
。这是一个反模式。而是承诺流"end"事件,然后返回该承诺。
PDFDocument
原型上定义函数,当然也不会在generateOnePDF
的每个调用上定义函数。你不妨直接调用SVGtoPDF
函数。
所以像这样:
const streamPromise = stream =>
return new Promise((resolve, reject) => {
let finalString = '';
stream.on('data', chunk => finalString += chunk);
stream.on('end', () => resolve(finalString));
stream.on('error', reject);
});
const generateOnePDF = async (ticket, orderData) => {
if (!ticket ) throw new Error('missing ticket data');
if (!orderData) throw new Error('missing order data');
const { payload, sequence, title } = ticket;
if (!payload ) throw new Error('missing payload data');
if (!sequence ) throw new Error('missing ticket sequence');
if (!title ) throw new Error('missing ticket book title');
const doc = new PDFDocument();
const stream = doc.pipe(new Base64Encode());
// logo
const logo = await fetchImage(`${url}/static/media/logo_157.c15ac239.svg`);
SVGtoPDF(doc, logo.toString(), 32, 40, {});
// ... etc ...
// ...
doc.end();
return streamPromise(stream);
});
这是由于使用async
函数作为new Promise
的executor参数,而不是在reject()
调用之后使用return
的组合造成的。如果没有这两种方法,您既不会reject()
承诺,也不会导致拒绝async
函数返回的承诺失败。修复两者,你会写
async function generateOnePDF(ticket, orderData) {
if (!ticket) throw new Error('missing ticket data');
if (!orderData) throw new Error('missing order data');
const { payload, sequence, title } = ticket;
if (!payload) throw new Error('missing payload data');
if (!sequence) throw new Error('missing ticket sequence');
if (!title) throw new Error('missing ticket book title');
const doc = new PDFDocument();
const logo = await fetchImage(`${url}/static/media/logo_157.c15ac239.svg`);
doc.addSVG(logo.toString(), 32, 40, {});
…
return new Promise((resolve, reject) => {
const stream = doc.pipe(new Base64Encode());
let finalString = '';
stream.on('data', chunk => {
finalString += chunk;
});
stream.on('end', () => {
resolve(finalString);
});
stream.on('error', reject);
doc.end();
});
});