我正在使用NodeJS创建一个快速端点,它将从存储在我服务器上的图像中检索元数据。我有以下端点逻辑代码:
/*
* 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
答案 0 :(得分:1)
使用async模块,它可以通过多种方式改善您的生活。
您遇到的问题是我看到的常见问题,而且您的循环是异步的,但您将其视为串行内容。
而不是执行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);
});
答案 1 :(得分:1)
您需要重新构建代码:
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
的回调函数时可用;这就是异步I / O在Node中的工作方式。
在该回调中,您将元数据添加到集合中。如果forEach
的迭代已经到达最后一个元素(i
是当前元素的索引,当它的值小于数组的大小时,它是最后一个元素),响应发送。
两个问题:
.DS_Store
是目录中的最后一个/唯一文件,则此代码将失败,因为它永远不会发回响应;我会留给你处理那个案子;)res.json
会返回200
状态,因此您无需指定;如果您做想要指定状态,则需要res.json(200, collection)
(参数交换)