Javascript作用域问题,对象在分配后没有数据

Javascript scoping issue, object has no data after assigning it

本文关键字:数据 分配 作用域 问题 对象 Javascript      更新时间:2024-01-09

我正在使用NodeJS创建一个express端点,该端点将从存储在服务器上的图像中检索元数据。我有以下端点逻辑代码:

/*
 * Gallery Controller
 */
var fs = require('fs'),
    _ = require('underscore'),
    im = require('imagemagick');
/**
 * List paths to images stored in selected gallery
 */
exports.list = function(req, res) {
    var dir = 'public/images/' + req.params.id;
    fs.readdir(dir, function(err, files) {
        if (err) return res.send({error: 'No gallery found with provided id'}, 404);
        if (files.length > 0) {
            var collection = [],
                myData = {};
            files.forEach(function(file) {
                if(file === '.DS_Store') return;
                im.readMetadata( dir + '/' + file, function(err, metadata) {
                    if (err) throw err;
                    myData = metadata;
                    console.log(myData); // logs as object with expected data
                });
                console.log(myData); // logs as empty {}
                collection.push(myData);
            });
            console.log(collection); // logs as [ {}, {} ]
            res.json(collection, 200);
        } else {
            res.json({error: 'Selected gallery is empty'}, 404);
        }
    });
};

我已经列出了日志在终端中的显示方式,为什么我会遇到这个范围界定问题?我似乎无法理解它。如果我尝试return metadata obj并将其分配给var,我会得到以下错误:TypeError: Converting circular structure to JSON

使用异步模块,它将在许多方面改善您的生活。

我看到你遇到的问题很常见,那就是你的循环是异步的,但你把它当作串行的。

您不想执行files.forEach,而是希望异步地循环它们,然后在循环完成后再执行一些操作。您可以使用async.each

async.each(files, function (file, next) {
  if (file === '.DS_Store') return next();
  im.readMetadata(path.join(dir, file), function (e, data) {
    collection.push(data);
    next(err);
  });
}, function (err) {
  if (err) throw err;
  console.log(collection);
});

作为替代方案,更合适的解决方案可能是使用async.map

async.map(files, function (file, next) {
  if (file === '.DS_Store') return next();
  im.readMetadata(path.join(dir, file), next);
}, function (err, collection) {
  if (err) throw err;
  console.log(collection);
});

您需要重组您的代码:

files.forEach(function(file, i) {
  if (file === '.DS_Store') return; // see text
  im.readMetadata( dir + '/' + file, function(err, metadata) { 
    if (err) throw err;
    collection.push(metadata);
    if (i === files.length - 1) {
      res.json(collection); // see text
    }
  });
});

原因是只有当调用readMetadata的回调函数时,元数据才可用;这就是Node中异步I/O的工作方式。

在该回调中,您将元数据添加到集合中。如果forEach的迭代已经到达最后一个元素(i是当前元素的索引,当它的值比数组的大小小一时,它是最后一个元件),则发送响应。

两个问题:

  • 如果.DS_Store是目录中最后一个/唯一的文件,则此代码将失败,因为它永远不会发回响应;我交给你来处理那个案子;)
  • 默认情况下,res.json将返回200状态,因此您不必指定它;如果确实要指定状态,则该状态需要为res.json(200, collection)(已交换参数)