javascript - Node.js 如何等待异步调用(readdir 和 stat)

标签 javascript node.js express asynchronous

我正在服务器端使用 post 方法来检索请求目录中的所有文件(非递归),下面是我的代码。

在不使用 setTimeout 的情况下,我很难用更新后的 pathContent 发回响应 (res.json(pathContent);)。

我知道这是由于所使用的文件系统方法(readdirstat)的异步行为导致的,需要使用某种回调、异步或 promise 技术。

我尝试将 async.waterfallreaddir 的整个主体一起用作一个函数,并将 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 并且代码几乎看起来像同步代码。

因此,您需要 readdirstat 的 promise 版本,可以通过 utilspromisify 创建> 核心库。

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/

相关文章:

javascript - 将变量传递给 setTimeout()

javascript - 如何修复基于文本输入的 jquery 站点重定向

javascript - 一个我看不懂的nodejs回调执行顺序问题的例子

javascript - 函数在异步方法完成之前返回 Node JS

javascript - 查找更新的 Node Facebook 模板

node.js - Passport.js twitter 身份验证导致 500 错误,req.session 未定义

javascript - 将全局样式表移动到 <head> 中的样式组件之上

javascript - 当在对象中找不到键时返回不同的值而不是未定义

node.js - 为 NodeJS 应用程序构建 docker 时找不到模块错误

javascript - Express 中更好的中间件链接