使用Node.js时返回含模板字符串的变量为何得到undefined?
问题原因与解决方案
核心问题
langs函数返回undefined的根本原因是异步操作的回调返回无效:
fs.readdir是异步IO函数,你在它的回调里return html_languages,这个return只属于回调函数本身,和外层的langs函数无关。langs函数没有任何顶层的return语句,所以调用时自然返回undefined。- 额外问题:
fs.readFile无法读取目录,当getId为null时,你传入data/会触发错误。
解决方案
方案1:使用回调函数处理异步结果
修改langs函数,通过回调传递结果:
function langs(path_data, callback) { let html_languages = `<ul>`; fs.readdir(path_data, function(err, list_file) { // 处理异步错误 if (err) { callback(err); return; } for(let i = 0; i < list_file.length; i++){ let fileName = list_file[i].split('.')[0]; html_languages += `<li><a href="/?id=${fileName}">${fileName}</a></li>`; } html_languages += `</ul>`; // 传递处理后的HTML callback(null, html_languages); }); }
然后修改HTTP服务中的调用逻辑,等待回调结果:
var app = http.createServer(function(request,response){ var queryData = new URL('http://localhost:3000' + request.url) if(queryData.pathname === '/'){ let getId = queryData.searchParams.get('id') let file_name; let title; let path_data = './data' // 调用langs并等待结果 langs(path_data, function(err, html_languages) { // 错误时显示默认内容 if (err) { html_languages = '<ul><li>目录加载失败</li></ul>'; } // 修正目录读取问题:指向具体文件而非目录 if(getId === null){ file_name = 'data/index.html'; // 创建对应的欢迎页文件 title = 'Welcome'; } else { title = getId; file_name = 'data/' + title + '.html'; } fs.readFile(file_name, 'utf8', function(err, description){ let desc = description || 'Hello, nodeJs.'; let template = ` <!doctype html> <html> <head> <title>WEB1 - ${title}</title> <meta charset="utf-8"> </head> <body> <h1><a href="/">WEB</a></h1> ${html_languages} <h2>${title}</h2> <p>${desc}</p> </body> </html> ` response.writeHead(200); response.end(template); }) }) } else { response.writeHead(404); response.end(); } }); app.listen(3000);
方案2:使用Promise + Async/Await(更现代的写法)
利用Promise封装异步操作,结合async/await让代码更简洁:
// 用Promise封装langs函数 function langs(path_data) { return new Promise((resolve, reject) => { let html_languages = `<ul>`; fs.readdir(path_data, function(err, list_file) { if (err) { reject(err); return; } for(let i = 0; i < list_file.length; i++){ let fileName = list_file[i].split('.')[0]; html_languages += `<li><a href="/?id=${fileName}">${fileName}</a></li>`; } html_languages += `</ul>`; resolve(html_languages); }); }); }
修改HTTP服务为async函数,用await等待结果:
var app = http.createServer(async function(request,response){ var queryData = new URL('http://localhost:3000' + request.url) if(queryData.pathname === '/'){ let getId = queryData.searchParams.get('id') let file_name; let title; let path_data = './data' let html_languages; try { // 等待langs的异步结果 html_languages = await langs(path_data); } catch(err) { html_languages = '<ul><li>目录加载失败</li></ul>'; } if(getId === null){ file_name = 'data/index.html'; title = 'Welcome'; } else { title = getId; file_name = 'data/' + title + '.html'; } fs.readFile(file_name, 'utf8', function(err, description){ let desc = description || 'Hello, nodeJs.'; let template = ` <!doctype html> <html> <head> <title>WEB1 - ${title}</title> <meta charset="utf-8"> </head> <body> <h1><a href="/">WEB</a></h1> ${html_languages} <h2>${title}</h2> <p>${desc}</p> </body> </html> ` response.writeHead(200); response.end(template); }) } else { response.writeHead(404); response.end(); } }); app.listen(3000);
方案3:使用Node.js内置的Promise版fs(最简洁)
Node.js提供了fs.promises模块,直接返回Promise,无需手动封装:
async function langs(path_data) { let html_languages = `<ul>`; // 直接用await读取目录 const list_file = await fs.promises.readdir(path_data); for(const file of list_file){ let fileName = file.split('.')[0]; html_languages += `<li><a href="/?id=${fileName}">${fileName}</a></li>`; } html_languages += `</ul>`; return html_languages; }
HTTP服务的调用逻辑和方案2一致,用async/await即可。
注意事项
- 始终处理异步操作的错误,避免服务因未捕获的异常崩溃。
fs.readFile只能读取文件,若要处理目录,需用fs.readdir或fs.stat判断类型。- 字符串拼接可以用
+=简化,或者用数组push后join,性能更优(文件数量多时更明显)。
内容的提问来源于stack exchange,提问作者jychoi
相关产品推荐
相关产品推荐

