我正在服务器端使用 post 方法来检索请求目录中的所有文件(非递归),下面是我的代码。
在不使用 setTimeout
的情况下,我很难用更新后的 pathContent
发回响应 (res.json(pathContent);
)。
我知道这是由于所使用的文件系统方法(readdir
和 stat
)的异步行为导致的,需要使用某种回调、异步或 promise 技术。
我尝试将 async.waterfall
与 readdir
的整个主体一起用作一个函数,并将 res.json(pathContent)
用作另一个,但它没有将更新后的数组发送到客户端。
我知道关于这个异步操作有成千上万的问题,但在阅读了很多帖子后无法弄清楚如何解决我的问题。
如有任何意见,我们将不胜感激。谢谢。
const express = require('express');
const bodyParser = require('body-parser');
const fs = require('fs');
const path = require('path');
var pathName = '';
const pathContent = [];
app.post('/api/files', (req, res) => {
const newPath = req.body.path;
fs.readdir(newPath, (err, files) => {
if (err) {
res.status(422).json({ message: `${err}` });
return;
}
// set the pathName and empty pathContent
pathName = newPath;
pathContent.length = 0;
// iterate each file
const absPath = path.resolve(pathName);
files.forEach(file => {
// get file info and store in pathContent
fs.stat(absPath + '/' + file, (err, stats) => {
if (err) {
console.log(`${err}`);
return;
}
if (stats.isFile()) {
pathContent.push({
path: pathName,
name: file.substring(0, file.lastIndexOf('.')),
type: file.substring(file.lastIndexOf('.') + 1).concat(' File'),
})
} else if (stats.isDirectory()) {
pathContent.push({
path: pathName,
name: file,
type: 'Directory',
});
}
});
});
});
setTimeout(() => { res.json(pathContent); }, 100);
});
最佳答案
最简单和最简洁的方法是使用 await
/async
,这样您就可以使用 promises 并且代码几乎看起来像同步代码。
因此,您需要 readdir
和 stat
的 promise 版本,可以通过 utils
的 promisify
创建> 核心库。
const { promisify } = require('util')
const readdir = promisify(require('fs').readdir)
const stat = promisify(require('fs').stat)
async function getPathContent(newPath) {
// move pathContent otherwise can have conflicts with concurrent requests
const pathContent = [];
let files = await readdir(newPath)
let pathName = newPath;
// pathContent.length = 0; // not needed anymore because pathContent is new for each request
const absPath = path.resolve(pathName);
// iterate each file
// replace forEach with (for ... of) because this makes it easier
// to work with "async"
// otherwise you would need to use files.map and Promise.all
for (let file of files) {
// get file info and store in pathContent
try {
let stats = await stat(absPath + '/' + file)
if (stats.isFile()) {
pathContent.push({
path: pathName,
name: file.substring(0, file.lastIndexOf('.')),
type: file.substring(file.lastIndexOf('.') + 1).concat(' File'),
})
} else if (stats.isDirectory()) {
pathContent.push({
path: pathName,
name: file,
type: 'Directory',
});
}
} catch (err) {
console.log(`${err}`);
}
}
return pathContent;
}
app.post('/api/files', (req, res, next) => {
const newPath = req.body.path;
getPathContent(newPath).then((pathContent) => {
res.json(pathContent);
}, (err) => {
res.status(422).json({
message: `${err}`
});
})
})
并且您不应该使用 +
(absPath + '/' + file
)连接路径,使用 path.join(absPath, file)
或 path.resolve(absPath, file)
代替。
并且您永远不应该以这样的方式编写代码:为请求执行的代码依赖全局变量,例如 var pathName = '';
和 const pathContent = [];
。这可能适用于您的测试环境,但肯定会导致生产问题。其中两个请求在“同时”
关于javascript - Node.js 如何等待异步调用(readdir 和 stat),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54045297/