我是nodejs的新手。我有一个for循环,它尝试从文件阵列一次上传一个文件。对于上传,它调用一个具有promise模式的方法。因此,for循环继续执行而不等待返回承诺,因此文件上载的顺序将丢失。有人可以帮我这个吗?
function uploadFiles(model, files){
var deferred = Q.defer();
var result = [];
async.eachSeries(files, function(currFiles, callback) {
async.eachSeries(currFiles, function(item, innerCallback) {
var fieldname = item.fieldname;
var tmp_path = item.path;
var _file = fs.readFileSync(tmp_path);
var fileuploaded = parseService.uploadFiles(model, fieldname,item.originalname, { base64 : new Buffer(_file).toString('base64')});
fileuploaded.then(function(data) {
result.push(data).then(function (res){
console.log('File upload success');
innerCallback();
}, function(err){
console.log('File upload fail');
innerCallback();
});
}, function(err) {
if (err) {
return deferred.reject(err);
}
console.log(result);
deferred.resolve(result);
});
}, function() {
callback();
});
return deferred.promise;
});
};
parseService.uploadFiles = function(fieldname, filename, file){
logger.verbose('On uploadFile');
var deferred = Q.defer();
var parseFile = new Parse.File(filename, file);
parseFile.save().then(function() {
return deferred.resolve();},
function(error) {
deferred.reject(err);
});
return deferred.promise;}
这就是我的方法的样子。目前for循环继续运行,文件异步上传,因此上传的顺序错误。
答案 0 :(得分:1)
假设您的初始files
是一个对象数组,并且您不想转移到ES7并转换代码,那么只需使用promises就可以大大简化代码。这使用了.reduce()
模式,承诺将上传序列化为一个接一个。
function uploadFiles(model, files) {
var results = [];
return files.reduce(function(p, item) {
return p.then(function() {
return fs.readFileAsync(item.path).then(function(fileData) {
let uploadData = {base64: new Buffer(fileData).toString('base64')};
return parseService.uploadFiles(item.fieldname, item.originalname, uploadData).then(function(data) {
results.push(data);
});
});
});
}, Promise.resolve()).then(function() {
return results;
});
}
parseService.uploadFiles = function (fieldname, filename, file) {
logger.verbose('On uploadFile');
var parseFile = new Parse.File(filename, file);
return parseFile.save();
}
然后你可以这样打电话给uploadFiles()
:
uploadFiles(model, arrayOfFileObjects).then(function(results) {
// results array here
}).catch(function(err) {
// some error here
});
这假设您已经拥有fs
模块的promisified版本。如果你不这样做,你可以像这样使用Bluebird:
const Promise = require('bluebird');
const fs = Promise.promisify(require('fs'));
或者,你可以像这样宣传这个fs.readFile()
函数:
fs.readFileAsync = function(file, options) {
return new Promise(function(resolve, reject) {
fs.readFile(file, options, function(err, data) {
if (err) return reject(err);
resolve(data);
});
});
}
如果您使用Bluebird作为承诺,那么您可以使用Promise.mapSeries()
,如下所示:
const Promise = require('bluebird');
const fs = Promise.promisify(require('fs'));
function uploadFiles(model, files) {
return Promise.mapSeries(files, function(item) {
return fs.readFileAsync(item.path).then(function(fileData) {
let uploadData = {base64: new Buffer(fileData).toString('base64')};
return parseService.uploadFiles(fieldname, item.originalname, uploadData).then(function(data) {
return data;
});
});
});
}
注意:
此实现假设您的原始files
参数是一个对象数组,每个对象包含有关要上载的文件的信息。如果它与此不同,那么请在您的问题中描述它究竟是什么。
您使用四个参数调用parseService.uploadFiles()
,但该函数只接受三个参数。我纠正了这一点。
请注意,uploadFiles()
和parseService.uploadFiles()
的实施不会自行手动创建任何新的承诺。他们只返回已经由他们使用的函数生成的promise。这避免了您使用另一个手动创建的承诺包装现有承诺的promise anti-pattern。当人们犯错误时会发生许多常见的错误,这是完全没必要的。
像你一样返回结果,在数组中累积内存中的所有文件。如果文件很大并且这里的功能似乎不需要,这可能会占用大量内存。
您传递了model
,但您没有任何实际使用它的代码。
如果您使用Bluebird promise库,那么您可以使用Promise.mapSeries()
代替.reduce()
来更简单地序列化您的操作。
答案 1 :(得分:1)
我使用babel
和async/await
来简化这一过程。一旦你的函数返回promises,你可以做这样的事情:
import readFile from 'fs-readfile-promise';
async function upload(files) {
const results = [];
for (let fname of files) {
let file = await readFile(fname, 'utf8');
let parsed = await parse(file);
results.push(parsed);
}
return results;
}
此外,只是一个副作用,但由于您的uploadFiles解析文件而不是上传它们(似乎),因此命名令人困惑。确保名称有意义。
答案 2 :(得分:-1)
var async = require('async');
function uploadFiles(currFiles) {
var deferred = Q.defer();
var result = []
async.eachSeries(currFiles, function(item, callback) {
var fieldname = item.fieldname;
var tmp_path = item.path;
var _file = fs.readFileSync(tmp_path);
var fileuploaded = parseService.uploadFiles(fieldname, item.originalname, {
base64: new Buffer(_file).toString('base64')
});
fileuploaded.then(function(data) {
result.push(data).then(function(res) {
logger.verbose('File upload success');
callback();
}, function(err) {
logger.verbose('File upload fail');
callback();
});
});
}, function(err) {
if (err) {
return deferred.reject(err);
}
console.log(result);
deferred.resolve(result);
});
return deferred.promise;
}
parseService.uploadFiles = function(fieldname, filename, file) {
logger.verbose('On uploadFile');
var deferred = Q.defer();
var parseFile = new Parse.File(filename, file);
parseFile.save().then(function() {
return deferred.resolve();
},
function(error) {
deferred.reject(err);
});
return deferred.promise;
}
uploadFiles
正在进行异步调用
除了undefined
之外,不能返回任何值。所以你必须使用
无论是回调还是承诺方式