2016-08-30 127 views
0

我是nodejs的新手。我有一個for循環,並嘗試從filearray一次上傳一個文件。對於上傳,它調用一個有承諾模式的方法。因此,for循環將繼續執行而不會等待返回的承諾,因此文件上傳的順序將丟失。任何人都可以幫助我嗎?Nodejs for loop change to同步

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

避免[deferred antipattern](http://stackoverflow.com/q/23803743/1048572)! – Bergi

+0

你不能使它同步,承諾總是異步的。你是否需要按順序運行上傳文件,或者平行運行也不錯? – Bergi

+0

我想現在順序運行它。因爲當我運行並行(異步)時,上傳順序正在變化 – user3339128

回答

-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. 每當 的JavaScript的異步行爲被調用您不能循環使用正常。
  2. 由於函數uploadFiles正在進行異步調用,所以它 無法返回除undefined之外的任何值。所以,你必須使用 無論是回調或承諾辦法
+0

您不應該在承諾中使用async.js。 – Bergi

+0

非常感謝你的回覆。只是最後一個問題。如果有2級循環會怎麼樣。 – user3339128

+0

@Bergi爲什麼不應該與承諾異步 – vkstack

1

假設您最初的files是對象的數組,你不想要移動到ES7和transpile你的代碼,那麼你可以簡化你的代碼有很多的只使用承諾。這使用.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版本。如果不這樣做,你可以使用藍鳥像這樣得到一個:

const Promise = require('bluebird'); 
const fs = Promise.promisify(require('fs')); 

或者,您可以promisify僅這一個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); 
     }); 
    }); 
} 

如果您使用的藍鳥承諾,那麼你可以使用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. 如果使用藍鳥承諾庫對於這一點,那麼你可以使用Promise.mapSeries(),而不是.reduce()更簡單的序列化操作。

1

我使用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解析,而不是把它們上傳(似乎)的文件。確保名稱有意義。

+0

但您正在使用同步文件讀取。這在任何node.js服務器進程中都是不允許的。 – jfriend00

+0

這是一個側面問題,如果他只是在該過程中進行上傳/解析,可能不適用,但我已經爲您更改了它。 –

+0

另外,'parse()'函數來自哪裏?OP沒有一個名爲的函數,你不會告訴他們如何做或上傳。我想這是一個簡化的概念答案,只涵蓋了一些概念,並且缺少很多其他部分,因此不是OP實際可以直接使用的東西。 – jfriend00