Node.js中如何为现有JSON附加JSON并在Pug中使用
我来帮你梳理下你遇到的问题:你写了两个处理评论回复的函数,发现不使用JSON.stringify的话,r.replies会显示成[Object],但用了之后在Pug里执行JSON.parse(replies)又报Unexpected end of JSON input错误。咱们一步步来解决。
问题表现与场景
当你直接把数组赋值给r.replies时,控制台会显示[Object],这只是字符串环境下的显示问题——JavaScript在直接打印对象数组时会默认转成[Object]字符串,但实际上r.replies还是一个有效的数组对象。
![未使用JSON.stringify时r.replies显示为[Object]](https://i.sstatic.net/1tWlE.png)
而使用JSON.stringify后出现解析错误,本质是生成的JSON字符串不完整,这和你代码里的异步操作未正确等待有关。
你提供的相关代码
function getContentReplies(author, permlink) { return new Promise((resolve, reject) => { getReply(author, permlink, function (err, resp) { if(err) reject(resp) resp.map(r => { r.body = marked(r.body) if(r.children > 0) return getContentReplies(r.author, r.permlink) .then(c => { let repliesArr = []; for (var a = 0; a < c.length; a++) { let i = { author: c[a].author, category: c[a].category, permlink: c[a].permlink, title: c[a].title, body: c[a].body } repliesArr.push(i); } r.replies = JSON.stringify(repliesArr); return r; }) else return r }) resolve(resp); }) }) } async function getComments (username, permlink) { console.log('-------------') return new Promise((resolve, reject) => { getReply(username, permlink, function (err, resp) { if (err) console.log(err) let promises = resp.map(r => { r.body = marked(r.body) if(r.children > 0) return getContentReplies(r.author, r.permlink) .then(c => { let repliesArr = []; for (var a = 0; a < c.length; a++) { let i = { author: c[a].author, category: c[a].category, permlink: c[a].permlink, title: c[a].title, body: marked(c[a].body) } repliesArr.push(i); } r.replies = JSON.stringify(repliesArr); return r; }) else return r }) Promise.all(promises) .then(results => { console.log(results); return results; }) .catch(e => { console.error(e); reject(e); }) resolve(resp); }) }) }
补充的Replies对象结构

问题根源分析
- 异步操作未正确等待:在
getContentReplies里,你用resp.map处理每个回复,其中包含异步的子回复加载,但你直接resolve(resp),根本没等这些异步操作完成。导致返回的resp中,很多r.replies还没被赋值,或者只赋值了一部分,后续JSON.parse自然会因为字符串不完整报错。 - getComments的resolve值错误:虽然你在
getComments里用了Promise.all(promises),但最后还是resolve了原始的resp,而不是处理完成后的results,等于前面的异步处理白做了,返回的还是没处理完回复的原始数组。 - 不必要的JSON转换:其实Pug可以直接访问JavaScript对象,你完全不需要把数组转成JSON字符串再解析——这多此一举还容易出问题,之前的
[Object]只是显示问题,不是对象本身无效。
解决方案
方案1:修复异步等待逻辑(若坚持用JSON转换)
先修改getContentReplies,确保等待所有异步操作完成后再返回结果:
function getContentReplies(author, permlink) { return new Promise((resolve, reject) => { getReply(author, permlink, async function (err, resp) { if(err) return reject(resp); // 用Promise.all等待所有回复的异步处理完成 const processedResp = await Promise.all(resp.map(async r => { r.body = marked(r.body); if(r.children > 0) { // 等待子回复加载完成 const c = await getContentReplies(r.author, r.permlink); const repliesArr = c.map(item => ({ author: item.author, category: item.category, permlink: item.permlink, title: item.title, body: item.body })); r.replies = JSON.stringify(repliesArr); } return r; })); resolve(processedResp); }) }) }
再修改getComments,确保返回处理完成后的结果:
async function getComments (username, permlink) { console.log('-------------') return new Promise((resolve, reject) => { getReply(username, permlink, function (err, resp) { if (err) { console.log(err); return reject(err); } const promises = resp.map(async r => { r.body = marked(r.body); if(r.children > 0) { const c = await getContentReplies(r.author, r.permlink); const repliesArr = c.map(item => ({ author: item.author, category: item.category, permlink: item.permlink, title: item.title, body: marked(item.body) })); r.replies = JSON.stringify(repliesArr); } return r; }) Promise.all(promises) .then(results => { console.log(results); resolve(results); // 返回处理后的结果,而非原始resp }) .catch(e => { console.error(e); reject(e); }) }) }) }
方案2:去掉JSON转换(更推荐)
既然Pug可以直接使用JavaScript对象,咱们直接赋值数组,省掉JSON序列化和反序列化的步骤:
修改两个函数中的r.replies = JSON.stringify(repliesArr);为r.replies = repliesArr;,同时修复异步等待逻辑,比如getContentReplies改成:
function getContentReplies(author, permlink) { return new Promise((resolve, reject) => { getReply(author, permlink, async function (err, resp) { if(err) return reject(resp); const processedResp = await Promise.all(resp.map(async r => { r.body = marked(r.body); if(r.children > 0) { const c = await getContentReplies(r.author, r.permlink); r.replies = c.map(item => ({ author: item.author, category: item.category, permlink: item.permlink, title: item.title, body: item.body })); } else { r.replies = []; // 给无回复的项加空数组,避免Pug遍历报错 } return r; })); resolve(processedResp); }) }) }
getComments同样修改resolve为results并去掉JSON相关代码,之后在Pug里直接遍历r.replies即可:
// Pug模板示例 each comment in comments .comment h3= comment.title .author= comment.author .body!= comment.body // 遍历子回复 if comment.replies.length > 0 .replies each reply in comment.replies .reply h4= reply.title .author= reply.author .body!= reply.body
这样既解决了[Object]的显示问题(控制台要查看完整结构,可用console.dir(r.replies)展开对象),又避免了JSON解析错误,代码也更简洁。
内容的提问来源于stack exchange,提问作者Gopesh Sharma

