Nodejs for loop更改为Synchronous

时间:2016-08-30 21:24:11

标签: javascript node.js callback promise q

我是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循环继续运行,文件异步上传,因此上传的顺序错误。

3 个答案:

答案 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;
            });
        });
    });
}

注意:

  1. 此实现假设您的原始files参数是一个对象数组,每个对象包含有关要上载的文件的信息。如果它与此不同,那么请在您的问题中描述它究竟是什么。

  2. 您使用四个参数调用parseService.uploadFiles(),但该函数只接受三个参数。我纠正了这一点。

  3. 请注意,uploadFiles()parseService.uploadFiles()的实施不会自行手动创建任何新的承诺。他们只返回已经由他们使用的函数生成的promise。这避免了您使用另一个手动创建的承诺包装现有承诺的promise anti-pattern。当人们犯错误时会发生许多常见的错误,这是完全没必要的。

  4. 像你一样返回结果,在数组中累积内存中的所有文件。如果文件很大并且这里的功能似乎不需要,这可能会占用大量内存。

  5. 您传递了model,但您没有任何实际使用它的代码。

  6. 如果您使用Bluebird promise库,那么您可以使用Promise.mapSeries()代替.reduce()来更简单地序列化您的操作。

答案 1 :(得分:1)

我使用babelasync/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;
}
  1. 无论何时异步行为,都不能使用normal for循环 javascript被调用。
  2. 由于函数uploadFiles正在进行异步调用 除了undefined之外,不能返回任何值。所以你必须使用 无论是回调还是承诺方式