Nodejs将循环更改为Synchronous

Nodejs for loop change to Synchronous

本文关键字:Synchronous 循环 Nodejs      更新时间:2023-09-26

我是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循环一直在运行,文件以异步方式上传,因此上传顺序错误。

我使用babelasync/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;
            });
        });
    });
}

注:

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

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

  3. 注意,我对uploadFiles()parseService.uploadFiles()的实现本身并没有手动创建任何新的承诺。它们只是返回它们使用的函数已经生成的承诺。这避免了您使用的promise反模式,即用另一个手动创建的promise包装现有的promise。当人们犯错误时,会出现许多常见的错误,这是完全没有必要的。

  4. 像您一样返回结果,将内存中的所有文件累积到一个数组中。如果文件很大,而且这里的功能似乎不需要,这可能会消耗大量内存。

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

  6. 如果使用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;
}
  1. 当的异步行为javascript被调用
  2. 由于函数uploadFiles具有异步调用,因此不能返回除undefined之外的任何值。所以你必须使用回调或promise方法