2016-08-30 4 views
0

저는 nodejs를 처음 사용합니다. 나는 for 루프를 가지고 있으며, 한 번에 하나의 파일을 filearray에서 업로드하려고 시도한다. 업로드의 경우 약속 패턴을 갖는 메소드를 호출합니다. 따라서 for 루프는 약속이 반환되기를 기다리지 않고 계속 실행되므로 파일을 업로드하는 순서가 손실됩니다. 누구든지 이걸 도와 줄 수 있니?Nodejs 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

[지연된 반 패턴] (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. 자바 스크립트의 비동기 동작이 호출 될 때마다 당신은 루프 정상 사용할 수 없습니다.
  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 버전을 가정합니다. 이처럼 그냥이 하나 개 fs.readFile() 기능을 promisify 수

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

을 또는 : 당신이하지 않으면, 당신은 다음과 같은 블루 버드를 사용하여 하나를 얻을 수 있습니다

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

은 당신이 블루 버드를 사용하는 경우

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

참고 : 약속, 당신은이 같은 Promise.mapSeries()을 사용할 수 있습니다

  1. 이 구현에서는 원래 files 인수가 업로드 할 파일에 대한 정보가 들어있는 객체 배열 인 것으로 가정합니다. 그것이 다른 것 인 경우 귀하의 질문에 정확하게 무엇인지 설명하십시오.

  2. 4 개의 인수를 사용하여 parseService.uploadFiles()을 호출했지만 함수는 3 개의 인수 만 허용합니다. 나는 그것을 바로 잡았다.

  3. 참고로 uploadFiles()parseService.uploadFiles()을 구현 한 경우 수동으로 새로운 약속을 만들지 않습니다. 그들은 단지 그들이 사용하는 함수에 의해 생성 된 약속을 반환합니다. 이렇게하면 기존 약속을 수동으로 작성한 약속으로 포장 한 곳에서 사용했던 promise anti-pattern을 피할 수 있습니다.사람들이 실수로 실수를 저질렀을 때 흔히 발생하는 버그가 많이 있습니다.

  4. 결과를 돌아 오는 것처럼 배열의 메모리에 모든 파일을 축적합니다. 파일이 크고 여기에 기능에 필요하지 않은 것 같으면 많은 메모리를 소비 할 수 있습니다.

  5. model을 전달했지만 실제로 사용하는 코드는 없습니다.

  6. 이 경우 Bluebird 약속 라이브러리를 사용하는 경우 .reduce() 대신 Promise.mapSeries()을 사용하여 작업을보다 간단하게 직렬화 할 수 있습니다.

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 서버 프로세스에서도 no-no입니다. – jfriend00

+0

그게 다다른 이슈이고, 그가 그 과정에서 업로드/파싱 만하고 있다면 적용되지 않을 수도 있지만, 어쨌든 당신을 위해 변경했습니다. –

+0

또한'parse()'함수의 출처는 어디입니까?OP는 이름이 붙은 함수가 없으므로이를 수행하거나 업로드하는 방법을 보여주지 않습니다. 나는 이것이 개념들만을 다루고 다른 많은 부분들을 빠뜨린 간소화 된 개념적 답변이므로 OP가 실제로 직접 사용할 수있는 것이 아닐 것이라고 생각합니다. – jfriend00