Express/Node.js |从 glob 调用中生成一个新的 JSON 对象

Express/Node.js | Generating a new JSON object from within a glob call

我是 JS 的新手,我认为我缺少有关对象状态存储和嵌套函数调用结构的一些基本知识。

我正在遍历一个包含许多 JSON 文件的目录,我想迭代加载这些文件并将内容解析为单个 JSON 对象。 我在函数内部正确解析了内容,但正如我所写的那样,对象状态不会在函数外部持续存在。

const glob = require('glob');

const retrieve = (query) => {
    const dataJSON = {};
    const json_path = saveDir + '/upload/' + query + '/'
    glob(json_path + '*.json', function (er, files) {
        for (index in files) {
            dataJSON[index] = require(files[index])
        }
        // Logging here returns the correct object
        console.log(dataJSON)
        return dataJSON
    });
    // Logging here returns an empty object
    console.log(dataJSON)
    return dataJSON

}

// I need to return the object here
app.get('/patent', (req, res) => {
    res.json(retrieve(req.query['q']))
});

两个问题,我如何在 glob 之外坚持 'dataJSON',以及我如何在 glob 之外 return 'files'?

例如。第二个问题

var example = glob("**/*.js", function (er, files) {
  // files is an array of filenames.

  return files
})

console.log(example)
>>['file1.json', 'file2.json', 'file3.json']

glob 看起来是异步的(因此回调/dataJson 是一个空对象)。您有两个选择:

答应吧

function retrieve (query) {
  return new Promise(function (resolve, reject) {
    glob(json_path + "*.json", function (err, files) {
      if (err) reject(files)
      else resolve(files)
    })
  })
}

app.get("/patent", (req, res) => {
  retrieve(req.query["q"])).then(files => res.json(files))
})

或者只是内联它和return你在回调中的响应

app.get("/patent", (req, res) => {
  glob(json_path + "*.json", function (err, files) {
    res.json(files)
  })
})

只是为了进一步重复 Tyler 的回答,因为您是 JS 的新手。 JavaScript 自异步以来严重依赖回调或承诺。这意味着您阅读代码的顺序不是(总是)代码执行的顺序。

您的代码如下所示:

  1. 定义函数检索
  2. 定义一些变量
  3. 调用 glob 读取一些文件
  4. 对这些文件做点什么//loggin here returns correct
  5. Return dataJSON 结果 //loggin here returns empty

但会按以下顺序执行:

  1. 定义函数检索
  2. 定义一些变量
  3. 调用 glob 读取一些文件
  4. Return dataJSON 结果 //loggin here returns empty
  5. 对这些文件做点什么//loggin here returns correct

原因是 glob 调用需要一些时间(列表中的第 3 个)。可能只有几毫秒,但您的代码会继续 运行。因此,当 glob 正在读取这些文件时,您的程序仍然 运行。当 glob 完成时,它 运行 回调函数。

回调基本就是"hey when you have read those files, then do this to them"

所以当您执行 console.log 时,您没有对文件进行任何操作。 Glob 仍在工作。

你可以在 console.log 秒内看到它。

const retrieve = (query) => {
    console.log('STEP 1')
    const dataJSON = {};
    const json_path = saveDir + '/upload/' + query + '/'
    glob(json_path + '*.json', function (er, files) {
        for (index in files) {
            dataJSON[index] = require(files[index])
        }
        // Logging here returns the correct object
        console.log('STEP 2')
        return dataJSON
    });
    console.log('STEP 3')
    // Logging here returns an empty object
    console.log(dataJSON)
    return dataJSON
}

所以你会认为输出是:

STEP 1
STEP 2
STEP 3

但它将是:

STEP 1
STEP 3
STEP 2

像 Tyler 提到的那样,有几种方法可以解决这个问题。

您可以继续回调。

const glob = require('glob');

const retrieve = (query, cb) => {
    const dataJSON = {};
    const json_path = saveDir + '/upload/' + query + '/'
    glob(json_path + '*.json', function (er, files) {
        for (index in files) {
            dataJSON[index] = require(files[index])
        }
        // Logging here returns the correct object
        console.log(dataJSON)
        cb(dataJSON)
    });
}

app.get('/patent', (req, res) => {
  retrieve(req.query['q'], function(resultsFromGlob) {
    res.json(resultsFromGlob) 
  })
});

请注意,我向您的检索函数添加了一个新参数。这就是回调,以便我们知道 glob 何时完成。现在我可以在路由器中使用该回调了。

另一种方法是使用 promises。

这是泰勒建议的方法,我同意。我个人更喜欢承诺而不是回调。

const retrieve = (query) => {
    return new Promise((resolve,reject) => {
      const dataJSON = {};
      const json_path = saveDir + '/upload/' + query + '/'
      glob(json_path + '*.json', function (er, files) {
          if (error) {
            reject(error)
          }

          for (index in files) {
              dataJSON[index] = require(files[index])
          }

          resolve(dataJSON);
      });
    })
}

app.get('/patent', (req, res) => {
  retrieve(req.query['q']).then(resultsFromGlob => {
    res.json(resultsFromGlob) 
  })  
});

这也让我可以选择使用 async/await

const retrieve = async (query) => {
    return new Promise((resolve,reject) => {
      const dataJSON = {};
      const json_path = saveDir + '/upload/' + query + '/'
      glob(json_path + '*.json', function (er, files) {
          if (error) {
            reject(error)
          }

          for (index in files) {
              dataJSON[index] = require(files[index])
          }

          resolve(dataJSON);
      });
    })
}

app.get('/patent', async (req, res) => {
  const resultsFromGlob = await retrieve(req.query['q'])        
  res.json(resultsFromGlob) 
});