![](/img/trans.png)
[英]JS (node.js) - If I make an asynchronous call but do not wait for it to complete will it be guaranteed to complete execution?
[英]Node.js how to wait on asynchronous call (readdir and stat)
我正在服務器端的post方法檢索所請求目錄中的所有文件(不是遞歸),下面是我的代碼。
我無法使用更新的pathContent
發送響應( res.json(pathContent);
)而不使用setTimeout
。
我知道這是由於所使用的文件系統方法的異步行為( readdir
和stat
),需要使用某種回調,異步或承諾技術。
我試圖使用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,代碼幾乎就像同步代碼。
因此,您需要一個可以通過utils
核心庫的promisify
創建的readdir
和stat
的promisified版本。
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.join(absPath, file)
或path.resolve(absPath, file)
。
而且你永遠不應該以代碼為請求執行的方式編寫代碼,繼續var pathName = '';
全局變量,如var pathName = '';
和const pathContent = [];
。 這可能在您的測試環境中有效,但肯定會導致生產中出現問題。 兩個請求在“同時”處理變量的位置
這里有一些選擇:
Sync
結尾)。 較慢,但代碼更改相當簡單,而且非常容易理解。 util.promisify
)為每個stat創建一個promise, Promise.all
等待所有統計信息完成。 之后,您可以使用異步函數並等待更容易閱讀的代碼和更簡單的錯誤處理。 (可能是最大的代碼更改,但它會使異步代碼更容易遵循) res.json
表單(最小的代碼更改,但非常容易出錯) 根據我收到的初始評論和參考,我使用了readdirSync和statSync,並且能夠使其工作。 我還將回顧其他答案,並了解實現此目的的其他方法。
謝謝大家的好意。
這是我的解決方案。
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;
// validate path
let files;
try {
files = fs.readdirSync(newPath);
} catch (err) {
res.status(422).json({ message: `${err}` });
return;
}
// set the pathName and empty pathContent
pathName = newPath;
pathContent.length = 0;
// iterate each file
let absPath = path.resolve(pathName);
files.forEach(file => {
// get file info and store in pathContent
let fileStat = fs.statSync(absPath + '/' + file);
if (fileStat.isFile()) {
pathContent.push({
path: pathName,
name: file.substring(0, file.lastIndexOf('.')),
type: file.substring(file.lastIndexOf('.') + 1).concat(' File'),
})
} else if (fileStat.isDirectory()) {
pathContent.push({
path: pathName,
name: file,
type: 'Directory',
});
}
});
res.json(pathContent);
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.