Nodejs将循环更改为Synchronous
Nodejs for loop change to Synchronous
我是nodejs的新手。我有一个for循环,它尝试一次从filearray上传一个文件。对于上传,它调用一个具有promise模式的方法。因此,for循环继续执行,而无需等待promise返回,因此文件上传的顺序也会丢失。有人能帮我吗?
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循环一直在运行,文件以异步方式上传,因此上传顺序错误。
我使用babel
和async/await
来简化这一点。一旦你的函数返回了承诺,你就可以这样做:
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解析文件而不是上传文件(看起来(。确保这些名字有意义。
假设您的初始files
是一个对象数组,并且您不想移动到ES7并转换代码,那么只需使用promise就可以大大简化代码。这使用了一个.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
模块的承诺版本。如果你不这样做,你可以用这样的蓝鸟得到一个:
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包装现有的promise。当人们犯错误时,会出现许多常见的错误,这是完全没有必要的。像您一样返回结果,将内存中的所有文件累积到一个数组中。如果文件很大,而且这里的功能似乎不需要,这可能会消耗大量内存。
您传入了
model
,但没有任何实际使用它的代码如果使用Bluebird promise库,那么可以使用
Promise.mapSeries()
而不是.reduce()
来更简单地序列化操作。
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;
}
- 当的异步行为javascript被调用
- 由于函数
uploadFiles
具有异步调用,因此不能返回除undefined
之外的任何值。所以你必须使用回调或promise方法
- jQuery:循环一个具有不同超时值的循环
- 在循环中分配json值时,值被覆盖
- 如何在下面的ES6循环中获得前面的文本
- 为什么“;未定义的“;在JavaScript中结束循环
- Javascript循环不会自我更新
- 如何使用jquery处理php循环通过元素
- 而循环只设置php中输入字段中的第一个值
- 循环遍历数组中的特定索引
- Javascript返回值只在循环中返回一次
- 按照选项卡索引的顺序循环一个jQuery选择
- 循环遍历以数组为值的Javascript对象
- 为什么JavaScript在for循环为3时向所有4发出警报
- 另一个ajax调用中的Jquery ajax调用在for循环中没有按预期工作
- 循环结束/推送到数组之前在页面上呈现EJS
- 循环比赛位置算法
- jQuery循环在特定位置暂停
- 我的javascript for循环不起作用
- 循环浏览多个身体背景图像
- W3C循环样式的JavaScript
- Nodejs将循环更改为Synchronous